Generate scale of two people for certain days

Asked

Viewed 106 times

0

I have a document to do every month, in this document is presented a three-day schedule ( Domingo, Fifth and Saturday ) every week, two different people will be scales on each day of this week:

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

The scale would be 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 registered persons what may vary for less or for more.

In this draw there can be no duplicates, ie:

08-03-2018 - Fulano 0 / Fulano 0

Nor can there 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 library Moment-weekdaysin, I was able to recover the Sundays, as Farms 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 time to draw people because if I use the code below, it will return seven drawn people being that I just need to two, Another thing that happens with the code below is that after the first loop the others contain the same values drawn in this first.

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

You can see working in stackblitz.with

1 answer

0

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

https://stackoverflow.com/a/18650169/8133067

[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();
});

I then got another answer to that same question in the English OS:

How to Randomize (shuffle) a Javascript array?
https://stackoverflow.com/a/2450976/8133067

To set up 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 where the Result is assembled, so:

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 people and shuffle array, to make the draw. Every day I want, I use two people from that list and remove them from there, when the list (array) runs out of people, I create a scrambled copy of the list of people again. I only check to make sure that the last person on the previous list is the same as the first person on the current list.

That way I think the result turned out the way you wanted. That’s what it was?

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

I thought of a new scheme, so in the case of repetition, instead of skipping the person, I recreate the draw list as many times as necessary until the last person doesn’t repeat himself. That way, I’ll always have a complete list, with all seven people, and no "double" anyone.

For that, 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 assembles 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 function getPessoaSorteada() recreated the array pessoasSorteadas when it was empty, but in Javascript apparently there is no way to pass an argument by reference (by ref), then, when I recreated the array within 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 selected:

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 also why before it worked: I had misspelled the function parameter name, I wrote pesoasSorteadas (with a’s' only), but in the body of the function I was using the correct name, pessoasSorteadas. How the code variable that called the function also had the same name, pessoasSorteadas, and was in a "global" scope, I think he was using within the function the variable that was defined outside of it.

The solution I found was to fill the array passed as 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 as a basis this answer in ONLYen, then the passage that used to be like 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’s 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 went like this, so:

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();
}
  • Still he’s the same people next 03/04/2018 - Fulano 1 / Fulano 2 03/08/2018 - Fulano 1 / Fulano 2 ... https://stackblitz.com/edit/js-qkkbxg?file=index.html

  • Weird, yesterday when I tested it was working fine, now there really is something wrong,.

  • I discovered the problem, and because yesterday worked, I will edit the answer (EDIT 2).

  • And now it’s worked?

Browser other questions tagged

You are not signed in. Login or sign up in order to post.