Home > Blockchain >  Unique array with exception
Unique array with exception

Time:04-11

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!

CodePudding user response:

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

CodePudding user response:

To begin, it appears you have a some syntax errors in your code- "names" as a variable is never created/initialized before you try using it, for instance. I assume it was meant to be "mainNames".

There should also be no spaces between the variable and method calls. e.g. 'names .length' should be 'names.length' and 'names [idx]' should be 'names[idx].

Aside from that, you're missing any way to check that the selected name isn't already in the list. Once you have your random index, convert it to a value, and then check if it fits the criteria you listed before moving on(if it's not already in the list). There are various ways to do that, but it's the most important step you're missing.

You then also need to add a way to check that your exception names appear only once. Again, there are several ways to do that but that step is currently missing entirely.

If you are getting stuck on problems like this I highly recommend writing out the steps you need to accomplish in pseudo-code (in comments in plain language) before jumping right into the real code. That can really help in keeping things straight.

CodePudding user response:

Do a random sort on the initial array and select the first 4 items. Then compare the items to the exceptions array make sure count is 1 or less. Keep repeating to a solution is found.

var mainNames = ["James", "Robert", "John", "Michael", "William", "David", "Peter", "Joseph"];
var exceptionNames = ["James", "Michael", "David", "Robert"];
let names = [], count = 4
while(count>1) {
  names = mainNames.map(i => ({ s: Math.random(), i })).sort((a,b) => a.s - b.s).map(({i})=> i).slice(4);
  count = names.filter(n => exceptionNames.includes(n)).length;
}
console.log(names.join(','))

  • Related