Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Joshua Thorley
Joshua Thorley
7,886 Points

Can anyone help me finish this script?

I'm trying to assign a range of products to a range of users. I want each user's products to be different and randomised, which I have managed to do.

The only issue with my code is that I need each product to be attributed at least once to at least one user. Can anyone see a way of doing that given what I have already written?

var users = ["user1", "user2", "user3", "user4"];

var products = ["p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15", "p16", "p17", "p18", "p19", "p20", "p21", "p22", "p23", "p24", "p25", "p26", "p27", "p28", "p29", "p30"];

function shuffle(array) {
  var currentIndex = array.length, temporaryValue, randomIndex;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
}

i = 0;
while (i < users.length) { 
    newArray = (shuffle(products)).slice(0, 10)
    console.log(users[i++] + ' products: ' + newArray);
}

Thank you so much

Joshua Thorley
Joshua Thorley
7,886 Points

The code so far gives me for example:

user1 products: p4,p6,p1,p16,p23,p10,p30,p20,p14,p2
user2 products: p22,p18,p10,p8,p3,p12,p23,p5,p29,p27
user3 products: p15,p1,p27,p30,p16,p3,p9,p11,p25,p4
user4 products: p26,p23,p14,p28,p21,p7,p10,p12,p20,p16

In this example, p13 hasn't been attributed to any users, and as I said, I need every product to have been used at least once.

2 Answers

I guess the easy solution would be to loop over the products instead of the users.

You could even shuffle the products before looping over them, and maybe set a maximum amount of products for each user, and remove them from the list when they reach that max, that way you get a more even distribution (if that's what you're looking for).

Maybe base the max on the number of products divided by the number of users, rounded up?

Erik Nuber
Erik Nuber
20,629 Points

I added quite a bit to the program but the larger the products list, the longer it takes to work. I have tested it on smaller lists up to p24 without much lag.

Here is what I did....

var users = ["user1", "user2", "user3", "user4"];

//var products = ["p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15", "p16", "p17", "p18", "p19", "p20", "p21", "p22", "p23", "p24", "p25", "p26", "p27", "p28", "p29", "p30"];

var products = ["p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8", "p9", "p10", "p11", "p12", "p13", "p14", "p15", "p16", "p17", "p18", "p19", "p20", "p21", "p22", "p23", "p24" ];

var newArray;
var arrayHolder = [];
var allProductsIn = false;

function shuffle(array) {
  var currentIndex = array.length, temporaryValue, randomIndex;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = array[currentIndex];
    array[currentIndex] = array[randomIndex];
    array[randomIndex] = temporaryValue;
  }

  return array;
}

Here was new, I took the array in, as it now an array of arrays, I used concat and apply to flatten it into just an array. Then, I sorted that array. Next, I created the "finalArr" which checks for duplicates and, if there isn't a duplicate it gets pushed into the array. Finally, I check to see if that final Array's length is equal to the length of the products array.

The idea here is that if the finalArr actually has the amount of items left in it equal to the amount of products, than we have found every item and we are good to go.

By all accounts, this should happen at some point. The problem is that the randomness of the creation of the arrayHolder makes it tough to complete the longer the products list.

function checkAllIn(arrayHolder) {

    var finalArr = [];
    var tempArr = arrayHolder;
    var concatArr = [].concat.apply([], tempArr);
    var sortArr = concatArr.sort();
    for (var i = 0; i < sortArr.length; i++) {
        if (finalArr.indexOf(sortArr[i]) == -1) {
            finalArr.push(sortArr[i]);
        }
    }
    console.log(finalArr);
    return (finalArr.length === products.length);
}

I created a loop to allow everything to work properly. It continues until allProductsIn is made true. This will then allow the information to be printed and leaves the while loop.

You will notice that I created an array of arrays. Each time newArray is made, it gets put into arrayHolder. This arrayHolder gets reset to empty if the arrayHolder didn't contain all of the products.

while (!allProductsIn) {

for (var i = 0; i < users.length; i++) { 
    newArray = (shuffle(products)).slice(0, 10);
    arrayHolder.push(newArray);
}

allProductsIn = checkAllIn(arrayHolder);

if (allProductsIn) {
    for (var k = 0; k<users.length; k++) {
         console.log(users[k] + ' products: ' + arrayHolder[k]);
    }
} else {
    arrayHolder = [];
}

}