How to swap objects array index based on property [Edit]

Hello to all community members. I have to deal with new challeng in javascript and i ask help to win.

Description

I have an array of objects containing students. The students have different attend like year 1 and so on until year 4.

var arrayStudents = [
{student_id: "11", student_name: "Messina", student_year_id: "1"},
{student_id: "6", student_name: "Di Ruzza", student_year_id: "1"},
{student_id: "17", student_name: "Simonetti", student_year_id: "1"},
{student_id: "14", student_name: "Ciaglia", student_year_id: "1"},
{student_id: "37", student_name: "Polito", student_year_id: "2"},
{student_id: "31", student_name: "Izzo", student_year_id: "2"},
{student_id: "45", student_name: "Cilia", student_year_id: "3"},
{student_id: "69", student_name: "Kutrolli", student_year_id: "4"},
{student_id: "71", student_name: "Lopez", student_year_id: "4"}
];
//console.log(arrayStudents.length);
//console.log(arrayStudents);

for this example the array contain 9 students: 4 of 1 year, 2 of 2 year, 1 of 3 year and 2 of 4 year.
So arrayT.length = 9

Now with this array a need to create a matrix array: Width = arrayT.lenght, Height 24

let’s create

function createGround(width) {

  var clonearrayStudents = arrayStudents.slice();
// clone are only my test i can not, but want to do
     var result = [];
       for (var i = 0; i < width; i++) {
           result[i] = clonearrayStudents
       }
     return result;
  }
var groundx = createGround(24);

Well done! Now i have 2D array.

  • reasoning
    Here i need to del with this delicious data for a destination goal.
    for now i have matrix array (2D) lenght 24 and width 9.
  • But any width on the matrix is the same. that is, it contains the same arry for each index. So now I need to shuffle each array by index.

For this we use the Fisher and Yates algorithm. So

shuffleBlock(groundx);

function shuffleBlock (ground){
  for (var y = 0; y < ground.length; y++){
  
    console.log(y);
    var cube = shufflex(ground[y]);
    var content =[];

    for(var j = 0; j < cube.length; j++) {
        console.log("cube[" + y + "][" + j + "] = "+ typeof cube[j] + JSON.stringify(cube[j]));
        content.push(cube[j]);
    }
    console.log('----------------');
   console.log(content);

}

//console.log(ground);

// Fisher and Yates algorithm
function shufflex(array) {
   var tmp, current, top = array.length;
   if (top)
   while (--top) {
   current = Math.floor(Math.random() * (top + 1));
   tmp = array[current];
   array[current] = array[top];
   array[top] = tmp;
                  }
   return array;
 }

Very nice output: Thank you Fisher and Yates algorithm!

  • reasoning
    At this step i heve what i desired, but come up a Problem where i hask to help from some expert.

The problem
look at console output of index 0 of the matrix array:

0 
cube[0][0] = object{"student_id":"71","student_name":"Lopez Mariangela","student_year_id":"4"} 
cube[0][1] = object{"student_id":"6","student_name":"Di Ruzza Alberto","student_year_id":"1"} 
cube[0][2] = object{"student_id":"17","student_name":"Simonetti Stefania","student_year_id":"1"} 
cube[0][3] = object{"student_id":"37","student_name":"Polito Eleonora","student_year_id":"2"} 
cube[0][4] = object{"student_id":"31","student_name":"Izzo Antonella","student_year_id":"2"} 
cube[0][5] = object{"student_id":"11","student_name":"Messina Emanuele","student_year_id":"1"} 
cube[0][6] = object{"student_id":"45","student_name":"Cilia Francesco","student_year_id":"3"}
cube[0][7] = object{"student_id":"69","student_name":"Kutrolli Eriselda","student_year_id":"4"} 
cube[0][8] = object{"student_id":"14","student_name":"Ciaglia Simone","student_year_id":"1"}

For my my project it is important that first year students are not consequent (like position [0][1] and [0][2]) and if they are, it is necessary to exchange positions with another index where the student_year_id property is greater than one like below

cube[0][0] = object{"student_id":"71","student_name":"Lopez Mariangela","student_year_id":"4"} 
cube[0][1] = object{"student_id":"6","student_name":"Di Ruzza Alberto","student_year_id":"1"} 
cube[0][3] = object{"student_id":"37","student_name":"Polito Eleonora","student_year_id":"2"} 
cube[0][2] = object{"student_id":"17","student_name":"Simonetti Stefania","student_year_id":"1"} 
cube[0][4] = object{"student_id":"31","student_name":"Izzo Antonella","student_year_id":"2"} 
cube[0][6] = object{"student_id":"45","student_name":"Cilia Francesco","student_year_id":"3"}
cube[0][5] = object{"student_id":"11","student_name":"Messina Emanuele","student_year_id":"1"} 
cube[0][7] = object{"student_id":"69","student_name":"Kutrolli Eriselda","student_year_id":"4"} 
cube[0][8] = object{"student_id":"14","student_name":"Ciaglia Simone","student_year_id":"1"}

HELP

i need to create a function with a loop inside for any index of the array and if find conseguent property move change position to reflect my needed.

So please help me to find the right path

Working Demo

Thank you in advance

Confusing from the off this.

First of all you have a height argument, that you never actually use.

Second you are passing in the arrayT.length. Why not just pass in the array, as you need to make a clone of it anyway

Essentially your code, with some name changes

const students = [
  {student_id: "11", student_name: "Messina", student_year_id: "1"},
  {student_id: "6", student_name: "Di Ruzza", student_year_id: "1"},
  {student_id: "17", student_name: "Simonetti", student_year_id: "1"},
  {student_id: "14", student_name: "Ciaglia", student_year_id: "1"},
  {student_id: "37", student_name: "Polito", student_year_id: "2"},
  {student_id: "31", student_name: "Izzo", student_year_id: "2"},
  {student_id: "45", student_name: "Cilia", student_year_id: "3"},
  {student_id: "69", student_name: "Kutrolli", student_year_id: "4"},
  {student_id: "71", student_name: "Lopez", student_year_id: "4"}
];


function createGround(width, height) {
  // height is not used in the function so why bother?
  var cloneSheeps = students.slice(); // cloneSheeps is a confusing name
  var result = [];

  for (var i = 0; i < width; i++) {
    result[i] = []; // this is unecessary as cloneSheeps is assigned below
    result[i] = cloneSheeps
  }
  return result;
}
var groundx = createGround(24, students.length);
// Output is an array with a length of 24. Each index has a reference to the same cloneSheeps array, yes?
// So should you do groundx[0].push({student_id: "79", student_name: "Bob", student_year_id: "2"})
// groundx[1], groundx[2] etc will all now have 79, Bob, Year 2

In a more modern way

function createGround(students, height) {
  const cloneStudents = Array.from(students) // clone of students array

  // use ...spread on a new Array(given size) to make an empty iterable array
  // then use map to create a new array
  return [...Array(height)].map(i => cloneStudents)
}

Is this what you want to achieve?
Have you used console.dir to check this delicious data. console.dir(groundx)?

A bit confusing, for me anyway.

I maybe getting the wrong end of the stick but couldn’t you do something like this

function shuffleStudents (students, height) {
  return [...Array(height)].map(i => shuffle(students))
}

The ‘shuffle’ callback being a function that returns a shuffled array.

1 Like

thank you for replay.
you wrote:
Confusing from the off this.
yes i know…

R - First of all you have a height argument, that you never actually use.
i have commented that higth are = i.

i don’t use in the example because the lenght of array are = var i in the loop.
So it is only an aesthetic problem.

Yours snippet are surely modern but the output is bad.

The problem was also not centered.
As the title of the topic states: how to swap …
so the problem are not modernize or aestetical, but how to swap object in array if there 2 conseguent student_year_id = 1.
OK?

var studentsArray = [
{student_id: "11", student_name: "Messina", student_year_id: "1"},
{student_id: "6", student_name: "Di Ruzza", student_year_id: "1"},
{student_id: "17", student_name: "Simonetti", student_year_id: "1"},
{student_id: "14", student_name: "Ciaglia", student_year_id: "1"},
{student_id: "37", student_name: "Polito", student_year_id: "2"},
{student_id: "31", student_name: "Izzo", student_year_id: "2"},
{student_id: "45", student_name: "Cilia", student_year_id: "3"},
{student_id: "69", student_name: "Kutrolli", student_year_id: "4"},
{student_id: "71", student_name: "Lopez", student_year_id: "4"}
];

i would like obtain follow
var desredStudentsArrayAfterLogicNeedToCreate = [
{"student_id":"71","student_name":"Lopez Mariangela","student_year_id":"4"} ,
{"student_id":"6","student_name":"Di Ruzza Alberto","student_year_id":"1"} ,
{"student_id":"37","student_name":"Polito Eleonora","student_year_id":"2"} ,
{"student_id":"17","student_name":"Simonetti Stefania","student_year_id":"1"} ,
{"student_id":"31","student_name":"Izzo Antonella","student_year_id":"2"} ,
{"student_id":"45","student_name":"Cilia Francesco","student_year_id":"3"},
{"student_id":"11","student_name":"Messina Emanuele","student_year_id":"1"} ,
{"student_id":"69","student_name":"Kutrolli Eriselda","student_year_id":"4"} ,
{"student_id":"14","student_name":"Ciaglia Simone","student_year_id":"1"}
];
// look property "student_year_id" no year 1 is ner to another 1

Hope more clear that before
Thank You

then why would you compare it to width?

Anyway. Let’s… boil it down to your question.

“How to swap if two students are both year 1”

There’s an underlying mathematical principle that you have to check first:
Are there enough non-first-year students that you CAN arrange them in such a way?

For example, take your 9 student array. If there are more than 5 year 1 students, it is mathematically impossible to place them such that no two first year students are adjacent members of the array. (It’s probably easier to check if the number of NON first-years is greater-or-equal to half the size of the array rounded down.)

Consider an array of width 4:
[X,X,X,X]
If I tell you 3 of the students are 1st year students, there is no possible way you can arrange them such that the condition holds true.

1 Like

sundsx you have edited your initial post, which isn’t helpful.

If you want to make amends it is better to make a new post.

Unless I am missing something, it is exactly the same as your output

const students = [
  {student_id: "11", student_name: "Messina", student_year_id: "1"},
  {student_id: "6", student_name: "Di Ruzza", student_year_id: "1"},
  {student_id: "17", student_name: "Simonetti", student_year_id: "1"},
  {student_id: "14", student_name: "Ciaglia", student_year_id: "1"},
  {student_id: "37", student_name: "Polito", student_year_id: "2"},
  {student_id: "31", student_name: "Izzo", student_year_id: "2"},
  {student_id: "45", student_name: "Cilia", student_year_id: "3"},
  {student_id: "69", student_name: "Kutrolli", student_year_id: "4"},
  {student_id: "71", student_name: "Lopez", student_year_id: "4"}
];

// your function
function createGround(width, height) {
//height here == i
  var cloneSheeps = students.slice();
     var result = [];
       for (var i = 0; i < width; i++) {
         result[i] = [];
         result[i] = cloneSheeps
       }
     return result;
  }

// my function
function createGround2(students, height) {
  const cloneStudents = Array.from(students) // clone of students array

  return [...Array(height)].map(i => cloneStudents)
}

var groundx = createGround(24, students.length);
// same outputs
console.dir(groundx)
console.dir(createGround2(students, 24))

so the problem are not modernize or aestetical, but how to swap object in array if there 2 conseguent

A fair point (I could have left it as a for loop), but there were issues with your code regardless, and those issues have the potential to have a knock on effect with the rest of your code — Broken window theory comes to mind

Tham you for answer

  • then why would you compare it to width?
    it’s not the focus, but for sake of dimostration:
function createGround(width, height) {
  console.log(height);
  var result = [];
     for (var i = 0; i < width; i++) {
       //  result[i] = [];
    //      shufflex(arrayT);
    //   result[i] = shufflex(arrayT);
       result[i] = getTiros(arrayT, height)
    //result[i]=shuffle3(arrayT);
         }
    return result;
  }
  
  function getTiros(arr, num) {
         var shuffled = arr.sort(() => 0.5 - Math.random());
         // const shuffled = shufflex(arr);
         // console.log("_----__");
         //  console.log(shuffled);
        // return shuffled.slice(1, num + 1);
  return shuffled;
            
}
  
var ground = createGround(24, arrayT.length);
console.log("ground"); 
console.log(ground);

The Outpu will be an array with lenght = 24, and in on any index a sub array with lenght 9 (students).

Q - There’s an underlying mathematical principle that you have to check first:
Are there enough non-first-year students that you CAN arrange them in such a way?

Of course.

Q For example, take your 9 student array. If there are more than 5 year 1 students, it is mathematically impossible to place them such that no two first year students are adjacent members of the array.

  • I agree but it won’t happen. At first if need to cover 9 position, surely at least 4 will be added.
    Q - (It’s probably easier to check if the number of NON first-years is greater-or-equal to half the size of the array rounded down.)

it’s easy on the examle: 4 = 1 . SUM of others = 5. We can proceed

So, once the math is avoided, how to proceed with the swap?

Thank You

Thank You for replay
Yes this snippet work like mine: same output.
I tried fir example and output was Array length 24, and for any index a subarray length 24 with sub sub array lenght 9.

But now it is correct, modern and elegant. Even if it doesn’t solve the question.
Thank you

You cannot guarantee this while using Fisher-Yates to shuffle the data.

If you’ve got 6 1st year students, and 210 2nd-through-4th year students, it is still possible to generate an array of 6 1st years and 3 others. (It’s unlikely in such a situation, but it’s still possible.)
And if it occurs, and your code is looking for a state in which you’ve solved this problem, your code will go into an infinite loop. So you should take it a bit more seriously than saying “it won’t happen”.

I am seriously! the code offered is for testing and trying to solve an unwanted eventuality. Not for production

Fisher-Yates change index of array. I use this algoritm for shuffle the chunks of array…
Also, infinite loop for now can’t happen. The loop is base on array length not while.
So thank You for advice

We can chart out an approximation of what the program needs to do, and try to come up with some code that achieves the same thing.

Here’s what we need to care about when looping through each student in the array:

  • Student 71, not a first year student so continue on
  • Student 6, a first year student, and next one (student 17) is a first year student. Find the next non first-year student (student 37) and swap with student 17.
  • Student 37, not a first year student so continue on
  • Student 17, a first year student and the next one (student 31) is not a first year, so continue on
  • Student 31, not a first year so continue on
  • Student 11, a first year student, and the next one (student 45) is not a first year so continue on
  • Student 45, not a first year student so continue on
  • Student 69, not a first year student so continue on
  • Student 14, a first year student and there is no next one so continue on

We now want to translate that logic into suitable looping code.

  • Use forEach to loop through each item in the array
  • If it’s not a year one student, return and process the next one
  • if we are on the last item of the array, return and process the next one
  • if the next item is not a year one student, return and process the next one
  • search after the next one for someone that’s not a first year
  • swap next item with the searched one

And that seems to fit all of the criteria that we need for the example that you provided.

More will be needed of course. For example, trouble happens when lots of first year students start of clumped together near the end of the array. And if the total number of first year students exceeds the total of all the others, then it’s not going to be possible to avoid clumping some first years together. But dealing with that kind of thing can come later when further testing shows the need for such concerns.

Oh, and turning that into computer code could look something like this:

students.forEach(function separateFirstYears(student, index, students) {
    if (!isFirstYearStudent(student)) {
        return;
    }
    if (isLastArrayItem(students, index)) {
        return;
    }
    if (!isFirstYearStudent(student[index + 1])) {
        return;
    }
    const nonFirstYearIndex = findNonFirstYearIndexAfter(students, index);
    swap(students, index, nonFirstYearIndex);
});

The rest is just implementing the details of those functions, and you’re left with easy to understand code that easily achieves the task.

Sorry to be that guy again, picking up on details — I had to check this.

Array.sort sorts the array in place, and does not return a copy MDN - Array.prototype.sort()

So for example

function getTiros(arr) {
  var shuffled = arr.sort(() => 0.5 - Math.random());
  return shuffled;        
}

var myArray = [1,2,3,4,5]
var myShuffled = getTiros(myArray)
// myArray has been changed
console.log(myArray) // random array e.g. [5,3,4,1,2]

If you don’t want to mutate the original array, then this might be an alternative

function getTiros(arr) {
  return Array.from(arr).sort(() => 0.5 - Math.random());
}
1 Like

Thank You @Paul_Wilkins for replay with delicious reasoning.
Sure something unwanted can happen. Obviously, all the ways must be tried and assessed the chances of success and eventually modify all the logic to obtain it. Then after if it’s worth it we proceed. It is important to experiment

thank you @rpg_digital for replay.
It is very important to be attentive to details.
in truth I have not yet decided on the final strategy. For example whether or not I want to change the initial array, or whether to use a randomization system or another. First I have to find, if there is, the solution for swapping index of arrays, then optimize all the code

thank you @Paul_Wilkins, interesting approach. I will try to implement the code and inform you about the results.

Good one, give it a go. I have a JSFiddle example with working code put aside, but as you’re well aware it’s important that you give it a go yourself.

By breaking the task down into separate functions, helps to ensure that each one is easy complete.

coughwhatiftheresnonextone.

[1,2,3,3,4,1,1,1,1]

Again. and again, the statement is made… you cannot… ignore… the mathematical… possibility/probability.

The only thing required for this logic path to fall down is two 1st year students at the end of an array.

My recommendation would be:

Randomize the first years as their own set of data.
Randomize the rest of the years as their own set of data.
Evenly distribute the first years throughout the set of the other-years.

or, if you want, choose N indexes I from the range of numbers 1…X/2, where N is the number of first years, and X is the number of students in total
If the range is exhausted, your condition is impossible;
else, insert a first year at each chosen I*2.

thank you @m_hutley, this was the first path i followed. This logic remain, for me, the last beach. But i want to try diffrent approach ,reasoning with community members.

For your info the array come from manual choice. The mathematical problem is averted. However, the statistical probability persists that two first years may be consequent. The goal is to assign these students in different sections and in this specific case 3 sections. So the array [i] [ long 9, after shuffled, will be in 3 different chunk. The main problem is that there cannot be two first year students in the same chunk.
graphics block attached!

Final destination! Goal