Search code examples
javascriptarraysunique-keyunique-array

Unique array with exception


I have really specific question. I spent many hours to make it work, but I wasn't successful.

Here is piece of my code, how it looks now:

var mainNames = ["James", "Robert", "John", "Michael", "William", "David", "Peter", "Joseph"];
var genNames = [];

var exceptionNames = ["James", "Michael", "David", "Robert"];

    for (var i = 0; i < 4; i++)
    {
        var idx = Math.floor(Math.random() * names .length);

        genNames.push(names [idx]);
    }

What I need to do?

  • mainNames is the main list from which I need to generate 4 unique names, without repetitions. Then, if in the generated list will be found 2 or more exceptionNames, repeat the generation of the mainNames list until the final generated list contain maximum of 1 exceptionNames and no repetitions of the mainNames.

  • Note: Generated list can contain 0 exceptionNames also, because the generation is random and does not have to generate exceptionNames as an extra names, because they already exists in the mainNames list.

How do I print the final list?

  • ${genNames[0]}, ${genNames[1]}, ${genNames[2]}, ${genNames[3]}

Here are some possible correct outputs:

  • James, William, John, Peter

  • Michael, William, Peter, John

  • Robert, John, William, Peter

  • David, John, William, Peter

  • Joseph, Peter, John, William

  • Note: As you can see in the possible correct outputs are no repetitions of the mainNames and no repetitions of the exceptionNames. And that’s how the above mentioned code should work.

Thanks for possible solutions/answers!


Solution

  • I suppose you could do it like this but it will be a little bit slow don't know how big your name arrays will be in the end.

    let mainNames = [
      'James',
      'Robert',
      'John',
      'Michael',
      'William',
      'David',
      'Peter',
      'Joseph',
    ]
    let genNames = []
    
    const exceptionNames = ['James', 'Michael', 'David', 'Robert']
    
    let exceptionNameAlreadyIncluded = false
    while (genNames.length < 4) {
      // if the variable exceptionNameAlreadyIncluded is true we filter our the exception names
      // to leave only the "normal" ones
      const namesToSearch = exceptionNameAlreadyIncluded
        ? mainNames.filter(
            (name) => !exceptionNames.some((exName) => exName === name),
          )
        : mainNames
        
      // belowe we get random name from our filtered ones push it to the genNamesArray
      // and we remove it from mainNames array so there is no duplicates
      const nameToAdd = namesToSearch[getRandomInt(namesToSearch.length)]
      mainNames = mainNames.filter((name) => name != nameToAdd)
      genNames = [...genNames, nameToAdd]
    
      // if the name we found using random is one of exception names we set our
      // exceptionNameAlreadyIncluded to true to not include any exception names in future randoms
      if (exceptionNames.some((name) => name === nameToAdd))
        exceptionNameAlreadyIncluded = true
    }
    
    console.log(genNames)
    
    function getRandomInt(max) {
      return Math.floor(Math.random() * max)
    }

    Faster solution

    let mainNames = [
      'James',
      'Robert',
      'John',
      'Michael',
      'William',
      'David',
      'Peter',
      'Joseph',
    ]
    let genNames = []
    
    const exceptionNames = ['James', 'Michael', 'David', 'Robert']
    
    let exceptionNameAlreadyIncluded = false
    while (genNames.length < 4) {
      const nameIndex = getRandomInt(mainNames.length)
      const isExceptionName = exceptionNames.some(
        (name) => name === mainNames[nameIndex],
      )
      if (!exceptionNameAlreadyIncluded || !isExceptionName) {
        genNames.push(mainNames[nameIndex])
      }
      if (isExceptionName) exceptionNameAlreadyIncluded = true
      mainNames.splice(nameIndex, 1)
    }
    
    console.log(genNames)
    
    function getRandomInt(max) {
      return Math.floor(Math.random() * max)
    }