3

I have a variable with 7 elements and I need to select 3 elements at random, but here are specific 3 element combinations i want it to avoid. How do I code this? (ex. I need it to avoid the combination [2,5,7] and [1,3,6])

This is what i have so far:

var Allregions = [
'1',
'2',
'3',
'4',
'5',
'6',
'7']


var ShowRegions = [];
    do {
      ShowRegions [ShowRegions.length] = Allregions.splice(
                            Math.floor(Math.random() * Allregions.length)
                          , 1)[0];
} while (ShowRegions.length < 3);

EDIT: I wanted to clarify something: The "numbers" are just placeholders, as they are actually calling elements from different parts of the code. i.e. 1 is actually something like "page banner". So the code I had written was fine in selecting 3 unique elements to populate a webpage, but it didn't allow me to control which 3 element combination is (not)shown.

HubrisRev5
  • 33
  • 5

3 Answers3

1

This function will return a random number without sequence of repeated letters & without excludes.

var allRegions = ['1', '2', '3', '4', '5', '6', '7'];

// to skip sequence of repeated letters.
var isSequence = /^([a-zA-Z0-9])\1+$/;
// to skip, if the combination has duplicates.
var hasDuplicates = /([a-zA-Z0-9]).*?\1/;

function isNotUniqueCombination(ex, rand) {
  for (var n = 0; n < ex.length; n++) {
    var e = ex[n];
    var combinations = [
      [e[0], e[1], e[2]],
      [e[0], e[2], e[1]],
      [e[1], e[0], e[2]],
      [e[1], e[2], e[0]],
      [e[2], e[0], e[1]],
      [e[2], e[1], e[0]]
    ];
    for (var i = 0; i < combinations.length; i++) {
      var com = combinations[i];
      if (com.join("") === rand.join("")) return true;
    }
  }
  return false;
}

/**
 * Return a random number without sequence of repeated letters & without excludes
 * @param {String} array 
 * @param {String} excludes 
 */
function getRandom(array, excludes) {

  // Random number array
  var randomNumber = [];

  var excludesMap = excludes.map(function(e) {
    return e.join("");
  });

  do {
    // Generate random number
    randomNumber = [
      array[Math.floor(Math.random() * array.length)],
      array[Math.floor(Math.random() * array.length)],
      array[Math.floor(Math.random() * array.length)]
    ];

  } while (excludesMap.indexOf(randomNumber.join("")) !== -1 || isSequence.test(randomNumber.join("")) || hasDuplicates.test(randomNumber.join("")) || isNotUniqueCombination(excludes, randomNumber));

  return randomNumber;
}

var excludes = [
  ['2', '5', '7'],
  ['1', '3', '6']
];


// Test Case
// =========
for (var i = 0; i < 1e3; i++) {
  var arr = getRandom(allRegions, excludes);

  var div = document.createElement('div');
  div.innerHTML = arr.join("");
  document.body.appendChild(div);

  // Check if the string contains a sequence of repeated letters of has duplicates
  if (isSequence.test(arr.join("")) || hasDuplicates.test(arr.join("")) || isNotUniqueCombination(excludes, arr)) {
    div.style.color = "#ff0000";
    div.innerHTML += " [ sequence of repeated letters found || duplicates found ]";
    document.body.appendChild(div);
    break;
  }

}
// ===
0xdw
  • 3,755
  • 2
  • 25
  • 40
  • I think this might be what I need, I just want to make sure that it is clear that the code I want does not deal with sequences of numbers (see my EDIT for the main post). I need basically a random 3 element selector to populate a "website", but one that excludes certain combinations, and repetitions( so no 3,3,3 etc). And thanks in advance for the help! – HubrisRev5 Oct 30 '17 at 11:04
  • @HubrisRev5 Check the answer now. I have updated it. – 0xdw Oct 30 '17 at 13:23
  • Thnaks for the update, but when I test the code it does not exclude repeated numbers (e.g., 227, or 661, etc). And the output keeps generating combinations, thus I doubt it is excluding duplicates. Technically there should only be 35 unique combinations, minus whatever combinations are excluded. So it is important that if i tell it to exclude (2,5,7) it understands I also mean exclude (5,2,7; 7,5,2, etc). Thanks in advance, and sorry if I am not being super clear... – HubrisRev5 Oct 30 '17 at 14:56
  • @HubrisRev5 Hi, Yeah im bit confused. Check it now. – 0xdw Oct 30 '17 at 16:03
  • one final issue, when i change back the placeholder numbers with the actual elements i am using the script hangs. I might be that the combination formula used does not understand if an element is instead of the number '1' it is 'apples-and-oranges' and instead of '2' is 'puppies-and-kittens' to treat these as a single units. As I mentioned, the numbers in my code here are here for simplicity sake, but they actually reflect specific elements. To save time, i might just have another code at the end that translates the numbers back into their corresponding names. – HubrisRev5 Oct 31 '17 at 15:13
1

You can pass the excluded sets to a function, if the result of random selection matches an excluded set call the function again

var Allregions = ["1","2","3","4","5","6","7"];

function randomNot(arr, not) {
  var curr = arr.slice();
  var ShowRegions = [];
  do {
    ShowRegions[ShowRegions.length] = curr.splice(
      Math.floor(Math.random() * curr.length), 1)[0];
  } while (ShowRegions.length < 3);
  return not.some(function(n) {
    return n === ShowRegions.join("")
  }) ? randomNot(arr, not) : ShowRegions
}

var not = ["257", "136"];

var res = randomNot(Allregions, not);

console.log(res);
guest271314
  • 1
  • 15
  • 104
  • 177
  • Hi, thanks for the solution! This code does almost everything I need, but has a slight issue. While it does exclude the "not" triplets, it only works in that specific order. i.e. 257 is ignored but not 527 or 725. How can i alter the code so that is ignores ALL combinations containing a specific 3 elements? – HubrisRev5 Oct 30 '17 at 14:51
  • That is a different requirement from description at original question – guest271314 Oct 30 '17 at 16:34
  • You can create an array of arrays containing all of the possible permutations of values, which would also include "275", "572", "752"; see https://stackoverflow.com/q/34013675/ – guest271314 Oct 30 '17 at 16:56
  • var not = [[257,275,527,572,725,752],[136,163,316,361,613,631]]; return not.some(function(curr){return curr.every(function(n){+ShowRegions.join("") === n})}); – guest271314 Oct 30 '17 at 17:12
0

Use IndexOf() Method to exclude the paticular number from the array list. Here is the modified code.

var Allregions = ['1','2','3','4','5','6','7'];

var ShowRegions = [];
randomNot(); // call the function to generate the number

function randomNot(){
  do {
    ShowRegions [ShowRegions.length] = Allregions.splice(Math.floor(Math.random() * Allregions.length), 1)[0];
  }
  while (ShowRegions.length < 3);
}
console.log("Response: "+ShowRegions);
var excludeNo = new Array("2,5,7", "1,3,6");

if(excludeNo.indexOf(ShowRegions) != -1){  
    console.log("Excluded Number");
    randomNot();
}
document.getElementById("exclude").innerHTML = ShowRegions;
<p id="exclude"></p>
vasanth
  • 715
  • 8
  • 18