0

I have an object like this:

var database = [
  {
    category: 'CPUs',
    id: 1,
    products: [Product, Product, Product] //Product is an object
  },
  {
    category: 'GPUs',
    id: 2,
    products: [Product, Product]
  }      
];

and so on..

I'd like to get 10 random products in total, non-repeating. There can be more than one from the same category, as long as they are different products. How can I do this? I tried this:

function getRandomFromObject(){
    var productsCollected = [];

    while(productsCollected.length < 10){
        var randomCategory = database[Math.floor(Math.random()*database.length)];
        var randomProduct = randomCategory.products[Math.floor(Math.random()*randomCategory.products.length)];
        productsCollected.push(randomProduct);
    }
    return productsCollected;
}
3
  • "Non-repeating" kind of makes it non-random, as the next value depends on the state of previous values. One approach for this could be to randomly sort (shuffle) the array and then just take the first 10 elements. Commented Apr 24, 2017 at 13:20
  • It would be simpler if your products were array of IDs, and Products would be a table of its own Commented Apr 24, 2017 at 13:21
  • get your random products, but add them to a set(). the set enforces uniqueness, so iterate over products until your set.length == 10 Commented Apr 24, 2017 at 13:23

1 Answer 1

3

Things become easier if you first concatenate all the products into one array, then shuffle that array and finally take the first 10 from that:

function shuffle(a) {
    for (let i = a.length; i; i--) {
        let j = Math.floor(Math.random() * i);
        [a[i - 1], a[j]] = [a[j], a[i - 1]];
    }
    return a;
}

function getRandomFromObject(count){
    return shuffle([].concat(...database.map(o => o.products))).slice(0, count);
}

var database = [
  {
    category: 'CPUs',
    id: 1,
    products: ['a', 'b', 'c'] //Product is an object
  },
  {
    category: 'GPUs',
    id: 2,
    products: ['d', 'e']
  },
  {
    category: 'GPUs',
    id: 3,
    products: ['f', 'g', 'h', 'i', 'j']
  }
];

console.log(getRandomFromObject(10).join(','));

Addendum: If you can have the same Product object occurring in different categories, then apply a Set to the concatenated array, so to eliminate these duplicates:

return shuffle([...new Set([].concat(...database.map(o => o.products)))]).slice(0, count);

ES5 Code

As you asked in comments for ES5, and the need to consider products with the same ISBN property as the same products, here is the code for that:

function shuffle(a) {
    for (var i = a.length; i; i--) {
        var j = Math.floor(Math.random() * i);
        var temp = a[i - 1];
        a[i - 1] = a[j];
        a[j] = temp;
    }
    return a;
}

function getRandomFromObject(count){
    var uniq = {}; // Unique list of products, keyed by ISBN
    database.forEach(function (o) {
        o.products.forEach(function (product) {
            uniq[product.isbn] = product;
        });
    });
    var products = []; 
    for (product in uniq) {
        products.push(uniq[product]);
    }
    return shuffle(products).slice(0, count);
}

var database = [
  {
    category: 'CPUs',
    id: 1,
    products: [{ isbn: 'a' }, { isbn: 'b' }, { isbn: 'c' }] //Product is an object
  },
  {
    category: 'GPUs',
    id: 2,
    products: [{ isbn: 'd' }, { isbn: 'a' }, { isbn: 'j' }] // has same isbn as in CPUs
  },
  {
    category: 'Others',
    id: 3,
    products: [{ isbn: 'e' }, { isbn: 'f' }, { isbn: 'g' }, { isbn: 'h' }, { isbn: 'i' }]
  }
];

console.log(getRandomFromObject(10));

Sign up to request clarification or add additional context in comments.

5 Comments

Hey, nice this is what I was looking for, however I need to support some older browsers so I can't use ES6 but I have another question: There are some products that are in two categories, but they have a different ID key, so running the array with the objects through a dedupe function doesn't work, how can I fix that?
You mean they have the same key? Or how else would you know they are dupes? (2) Can you support ES5?
1) The product is the same in the two categories, but it has a different ID in each one. But there's a field called ISBN that can be used to compare them, but I don't know how to do that. 2) Yes I can support ES5
See addition in my answer.
Thanks! Just a small thing, I had to change this: products.push(product) for this: products.push(uniq[product]) because I was getting the ISBN instead of the objects.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.