[removed] randomly pair items from array without repeats

前端 未结 5 2047
执笔经年
执笔经年 2021-01-05 09:03

I am trying to make a very basic \"secret santa\" generator as one of my first Javascript projects. I have searched for hours for a solution to this problem but so far noth

相关标签:
5条回答
  • 2021-01-05 09:26

    There is a multitude of ways you can achieve this.

    The fastest to code, but not necessarily the randomest is:

    var names = ["Sean","Kyle","Emily","Nick","Cotter","Brian","Jeremy","Kimmy","Pat","Johnny"];
    function getPicks(names) {
      return names.slice(0).sort(function(){ return Math.random()-0.5 }).map(function(name, index, arr){
        return name + " gets " + arr[(index+1)%arr.length];
      });
    }
    getPicks(names);
    

    This is not very random because the shuffling isn't very good and also because you get a single cycle each time. There can be no two cycles A->B->C->A D->E->D.

    If you want it to have a random number of cycles of variable length, you can split the names array in several arrays and do the above for each of them, then concatenate the results (see elclanrs).

    Finally, the last solution is for each person to pick a person at random and if it's the same one, simply pick again. If the last name remaining in both arrays is the same, simply swap it with another pair.

    var names = ["Sean","Kyle","Emily","Nick","Cotter","Brian","Jeremy","Kimmy","Pat","Johnny"];
    
    var a = names.slice(0);
    var b = names.slice(0);
    var result = [];
    while (a.length > 1) {
      var i = extractRandomElement(a);
      var j = extractRandomElement(b);
    
      while (i===j) {
        b.push(j);
        j = extractRandomElement(b);
      }
      result.push({ a:i, b:j });
    }
    if (a[0] === b[0]) {
      result.push({ a:a[0], b:result[0].b });
      result[0].b = a[0];
    } else {
      result.push({ a:a[0], b:b[0] });
    }
    var pairs = result.map(function(item){ return item.a + ' gets ' + item.b});
    
    
    function extractRandomElement(array) {
      return array.splice(Math.floor(Math.random()*array.length),1)[0];
    }
    
    0 讨论(0)
  • 2021-01-05 09:30

    Create two arrays with the names, shuffle them, and make sure you don't pick the same name from both arrays :

    var names = ["Sean","Kyle","Emily","Nick","Cotter","Brian","Jeremy","Kimmy","Pat","Johnny"];
    
    if (names.length % 2 != 0) {
        alert("You must have an even number of names. You currently have " + names.length + " names.");
    } else {
        var arr1 = names.slice(), // copy array
            arr2 = names.slice(); // copy array again
    
        arr1.sort(function() { return 0.5 - Math.random();}); // shuffle arrays
        arr2.sort(function() { return 0.5 - Math.random();});
    
        while (arr1.length) {
            var name1 = arr1.pop(), // get the last value of arr1
                name2 = arr2[0] == name1 ? arr2.pop() : arr2.shift();
                //        ^^ if the first value is the same as name1, 
                //           get the last value, otherwise get the first
    
            console.log(name1 + ' gets ' + name2);
        }
    }
    

    FIDDLE

    0 讨论(0)
  • 2021-01-05 09:30

    If you don't need to keep the original array you can remove the names as they get selected and each time you pick a name check that it isn't an empty string before pushing it to the next array.

    0 讨论(0)
  • 2021-01-05 09:34

    I would suggest a different approach. Shuffle, split, and zip, no mutation:

    var splitAt = function(i, xs) {
      var a = xs.slice(0, i);
      var b = xs.slice(i, xs.length);
      return [a, b];
    };
    
    var shuffle = function(xs) {
      return xs.slice(0).sort(function() {
        return .5 - Math.random();
      });
    };
    
    var zip = function(xs) {
      return xs[0].map(function(_,i) {
        return xs.map(function(x) {
          return x[i];
        });
      });
    }
    
    // Obviously assumes even array
    var result = zip(splitAt(names.length/2, shuffle(names)));
    //^
    // [
    //   [ 'Nick', 'Kimmy' ],
    //   [ 'Sean', 'Johnny' ],
    //   [ 'Kyle', 'Brian' ],
    //   [ 'Cotter', 'Pat' ],
    //   [ 'Emily', 'Jeremy' ]
    // ]
    
    0 讨论(0)
  • 2021-01-05 09:40

    I'm a tad late, but thought I'd throw my answer in here. It essentially does the same thing @adeneo's does, but it uses the same basic code as OP:

    var names = ["Sean","Kyle","Emily","Nick","Cotter","Brian","Jeremy","Kimmy","Pat","Johnny"];
        pickpool = names.slice(0); // Slice the array at the first element to copy it by value
    
    var used = [];
    var picks = [];
    
    if (names.length % 2 != 0) {
        alert("You must have an even number of names. You currently have " + names.length + " names.");
    }
    
    for( var i = 0; i < names.length; i++){
    
        var random = Math.floor(Math.random()*pickpool.length)
    
        if(names[random] == names[i]) {
            // names[random] = names[random++];
            picks.push(names[i] + " gets " + pickpool[random++]);
            pickpool.splice(random++,1);
        } else {
            picks.push(names[i] + " gets " + pickpool[random]);
            pickpool.splice(random,1);
        }
    }
    console.log("picked array: ");
    for(var k=0; k<picks.length; k++) {
        console.log(picks[k]);
    }
    

    http://jsfiddle.net/SNJpC/

    0 讨论(0)
提交回复
热议问题