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?

How do I print the final list?

Here are some possible correct outputs:

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)
    }