Generate scale of two people for certain days

0

I have a document to do every month; a three-day ( Sunday , Quinta and Saturday ) each week two different people will be scaled every day of this week:

let Pessoas = ["Fulano 0", "Fulano 1", "Fulano 2", "Fulano 3", "Fulano 4", "Fulano 5", "Fulano 6"]

The scale would look like:

08-03-2018 - Fulano 0 / Fulano 5
10-03-2018 - Fulano 6 / Fulano 2
11-03-2018 - Fulano 1 / Fulano 3

The problem is that the object Pessoas does not have a fixed size, and at the moment it has seven people registered which can vary for less or more.

In this draw there can be no duplicates, that is:

08-03-2018 - Fulano 0 / Fulano 0

There can not be as in the example below, without all possible combinations being reached.

08-03-2018 - Fulano 0 / Fulano 5
10-03-2018 - Fulano 6 / Fulano 2
11-03-2018 - Fulano 0 / Fulano 5

Using the moment-weekdaysin library, I was able to recover the Sundays , the Quintas and the Saturdays of the current month with the code below:

let datas = moment().weekdaysInMonth([0, 4, 6]);
datas.forEach(data => {
  Resultado.push({
    data: data.format('L'),
  })
});

The return is:

[
  { "data": "03/01/2018" }, { "data": "03/03/2018" },
  { "data": "03/04/2018" }, { "data": "03/08/2018" },
  { "data": "03/10/2018" }, { "data": "03/11/2018" },
  { "data": "03/15/2018" }, { "data": "03/17/2018" },
  { "data": "03/18/2018" }, { "data": "03/22/2018" },
  { "data": "03/24/2018" }, { "data": "03/25/2018" },
  { "data": "03/29/2018" }, { "data": "03/31/2018" }
]

My problem is when it's time to draw people because if I use the code below, it will return seven people raffled and I need only two , something else that happens with the code below is that after the first loop the others contain the same values drawn in that first one.

escala: Pessoa.sort(_ => {
  return .7 - Math.random()
})
  

You can see it working at stackblitz.com

    
asked by anonymous 08.03.2018 / 15:07

1 answer

0

I have seen that this method you are using to "shuffle" the array, is not very indicated, see this post from the English OS:

  

link

     

[community edit: This answer is incorrect; see comments. It is being left here for future reference because the idea is not that rare.]

[1,2,3,4,5,6].sort(function() {
  return .5 - Math.random();
});

So I took another answer to the same question in English OS:

  

How to randomize (shuffle) a JavaScript array?
link

To mount this function:

function shuffle(array) {
  var currentIndex = array.length;
  var newArray = array.slice();
  var temporaryValue, randomIndex;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

    // And swap it with the current element.
    temporaryValue = newArray[currentIndex];
    newArray[currentIndex] = newArray[randomIndex];
    newArray[randomIndex] = temporaryValue;
  }

  return newArray;
}

I also created this other function:

function getPessoaSorteada(pessoas, pessoasSorteadas) {
  if (pessoasSorteadas.length == 0)
    pessoasSorteadas = shuffle(pessoas);
  return pessoasSorteadas.pop();
}

And I modified that part of your code in which the Result is mounted, thus:

var pessoasSorteadas = [];
var pessoa1, pessoa2;
datas.forEach(data => {
  pessoa1 = getPessoaSorteada(Pessoa, pessoasSorteadas);
  pessoa2 = getPessoaSorteada(Pessoa, pessoasSorteadas);
  if (pessoa2==pessoa1)
    pessoa2 = getPessoaSorteada(Pessoa, pessoasSorteadas);
  Resultado.push({
    data: data.format('L'),
    diaSemana: semana[data.day()],
    escala: [pessoa1, pessoa2]
  })
});

Explaining:
I create a copy of the array of people and the pack, to make the lottery. Every day I want to use two people from this list and remove them from there, when the array is out of people, I create a scrambled copy of the list of people again. Just Czech so that the last person on the previous list does not happen to be the first one on the current list.

That way I think the result was the way you wanted it. Was that right?

EDIT:
I realized there was a hole in my first suggestion: if, by coincidence, the last person drawn from one round was equal to the first person on the next round list, I simply ignored that person and used the next one on the list, there would be repetition, but then that person would not be escalated in that next round, ie it would be a round of 6 people only, instead of 7.

I thought of a new scheme, so that in the case of repetition, instead of jumping the person, I recreate the list of drawn, as many times as necessary, until the last person does not repeat itself. So, I will always have a complete list, with 7 people, and no one "double".

For this, I changed this function:

function getPessoaSorteada(pessoas, pessoasSorteadas, ultimaPessoaSorteada) {
  if (pessoasSorteadas.length == 0) {
    pessoasSorteadas = shuffle(pessoas);
    while (ultimaPessoaSorteada == pessoasSorteadas[pessoasSorteadas.length - 1])
      pessoasSorteadas = shuffle(pessoas);
  }
  return pessoasSorteadas.pop();
}

And I also changed the section that mounts the result:

var pessoasSorteadas = [];
var pessoa1, pessoa2;
datas.forEach(data => {
  pessoa1 = getPessoaSorteada(Pessoa, pessoasSorteadas, pessoa2);
  pessoa2 = getPessoaSorteada(Pessoa, pessoasSorteadas, pessoa1);
  Resultado.push({
    data: data.format('L'),
    diaSemana: semana[data.day()],
    escala: [pessoa1, pessoa2]
  })
});

EDIT 2:
There was another problem in the code I proposed: The getPessoaSorteada() function recreated the pessoasSorteadas array when it was empty, but in Javascript there is apparently no way to pass a argument by reference by ref ), then when I recreated the array inside the function, it was not returned to the code that called the function, so the array was being recreated all the time, causing several repetitions in the people drawn: / p>

function getPessoaSorteada(pessoas, pessoasSorteadas, ultimaPessoaSorteada) {
  // ...
    // Essa linha criava um novo array, que não era 
    // devolvido através do argumento da função.
    pessoasSorteadas = shuffle(pessoas);
  // ...
}

I had found it strange because when I tested the code it worked, but then I discovered it also because before it worked: I had written the function parameter name wrong, I typed pesoasSorteadas (with a 's' only), but in the function body I was using the correct name, pessoasSorteadas . Since the code variable that called the function also had the same name, pessoasSorteadas , and was in a "global" scope, I think it was using inside the function the variable that was defined outside of it.

The solution I found was to fill the array passed as an argument to the function with the values of the new array, so the array is returned by the argument, since it is not recreated, and therefore remains the same object. For this I used this response in the SO en , then the excerpt that used to be this:

function getPessoaSorteada(pessoas, pessoasSorteadas, ultimaPessoaSorteada) {
  // ...
    // Essa linha criava um novo array, que não era 
    // devolvido através do argumento da função.
    pessoasSorteadas = shuffle(pessoas);
  // ...
}

Now it looks like this:

function getPessoaSorteada(pessoas, pessoasSorteadas, ultimaPessoaSorteada) {
  // ...
    // Agora ao invés de criar um novo array em cima do argumento
    // 'pessoasSorteadas', o mesmo objeto (array) passado para a
    // função é populado com os elementos do novo array, com as 
    // pessoas sorteadas, criado pela função shuffle(pessoas).
    pessoasSorteadas.push.apply(sorteadas, shuffle(pessoas));
  // ...
}

The whole function looks like this, then:

function getPessoaSorteada(pessoas, pessoasSorteadas, ultimaPessoaSorteada) {
  if (pessoasSorteadas.length == 0) {
    pessoasSorteadas.push.apply(pessoasSorteadas, shuffle(pessoas));
    while (ultimaPessoaSorteada == pessoasSorteadas[pessoasSorteadas.length - 1])
      pessoasSorteadas.push.apply(pessoasSorteadas, shuffle(pessoas));
  }
  return pessoasSorteadas.pop();
}
    
08.03.2018 / 16:41