Reorder Array based on value in an object

52 Views Asked by At

please is there a way to turn subject1 to subject2. if you notice, the chemistry in the subject2 occurs at interval of 3.


const subjects = [
    { "name": "Math", "score": 32 },
    { "name": "Chemistry", "score": 17 },
    { "name": "English", "score": 17 },
    { "name": "Math", "score": 55 },
    { "name": "Chemistry", "score": 21 },
    { "name": "Chemistry", "score": 75 },
    { "name": "Chemistry", "score": 45 },
    { "name": "Physics", "score": 9 },
    { "name": "Physics", "score": 4 },
    { "name": "Physics", "score": 21 },
    { "name": "Physics", "score": 11 },
    { "name": "Physics", "score": 21 },
    { "name": "Physics", "score": 11 },
    { "name": "Physics", "score": 21 },
    { "name": "Physics", "score": 11 },
    { "name": "Physics", "score": 11 },
];

const Newsubjects = [
    { "name": "Math", "score": 32 },
    { "name": "English", "score": 17 },
    { "name": "Chemistry", "score": 17 },  *********
    { "name": "Math", "score": 55 },
    { "name": "Physics", "score": 9 },
    { "name": "Chemistry", "score": 21 },  *********
    { "name": "Physics", "score": 4 },
    { "name": "Physics", "score": 21 },
    { "name": "Chemistry", "score": 75 },   *********
    { "name": "Physics", "score": 21 },
    { "name": "Physics", "score": 11 },
    { "name": "Chemistry", "score": 45 },   *********
    { "name": "Physics", "score": 21 },
    { "name": "Physics", "score": 11 },
    { "name": "Physics", "score": 21 },
    { "name": "Physics", "score": 11 },
];

SO i created this simple for loop, It works well

const subjects = [
    { "name": "Math", "score": 32 },
    { "name": "Chemistry", "score": 17 },
    { "name": "English", "score": 17 },
    { "name": "Math", "score": 55 },
    { "name": "Chemistry", "score": 21 },
    { "name": "Chemistry", "score": 75 },
    { "name": "Chemistry", "score": 45 },
    { "name": "Geo", "score": 9 },
    { "name": "Physics", "score": 4 },
    { "name": "Arab", "score": 21 },
    { "name": "Hausa", "score": 11 },
    { "name": "Yoruba", "score": 21 },
    { "name": "Commerce", "score": 11 },
    { "name": "Spanish", "score": 21 },
    { "name": "German", "score": 11 },
    { "name": "Gov", "score": 11 },
];

let filteredArray = subjects.filter(value => value.name == "Chemistry");
let filteredArray2 = subjects.filter(value => value.name != "Chemistry");
//console.log(filteredArray)
for (let i = 0; i <= filteredArray2.length; i++)
{
       if (i % 3 == 0 ) {
            filteredArray2.splice( i, 0, { "name": "Agric", "score": 11 } )
       }

}
console.log(filteredArray2)

Then whenever I try to change the value of the splice to the filteredArray, so that it won't be hardcoded { "name": "Agric", "score": 11 } I added another for loop because I wanted to add the value from the filtered array

for (let i = 0; i <= filteredArray2.length; i++)
{
    for (let j = 0; j <= filteredArray.length; j++)
    {
       if (i % 3 == 0) {
        filteredArray2.splice(i,0, filteredArray[j],)
        }
  
    }
}

Please am open to suggestions or any idea As the goal is to just rearrange the array based on the chemistry object and make it occur with the interval of 3

Thank you for your time

2

There are 2 best solutions below

5
Alexander Nenashev On BEST ANSWER

You could add some fixes to your code.

  • since you insert an element to an array you should increment the loop index (i++) so the loop would accommodate the new array length.

  • based on your desired output you should insert at the loop index minus 1 and skip the zero index from moonwave99's answer - use i % 3 === 2.

  • to get an element from the chemistry array use Array::shift() to get elements

  • break the loop if there's no more chemistry elements to insert.

let filteredArray = subjects.filter(value => value.name == "Chemistry");
let filteredArray2 = subjects.filter(value => value.name != "Chemistry");


for (let i = 0; i <= filteredArray2.length && filteredArray.length; i++){
  i % 3 === 2 && filteredArray2.splice( i++ , 0, filteredArray.shift());
}

filteredArray2.forEach(item => console.log(JSON.stringify(item)));
<script>
const subjects = [
    { "name": "Math", "score": 32 },
    { "name": "Chemistry", "score": 17 },
    { "name": "English", "score": 17 },
    { "name": "Math", "score": 55 },
    { "name": "Chemistry", "score": 21 },
    { "name": "Chemistry", "score": 75 },
    { "name": "Chemistry", "score": 45 },
    { "name": "Physics", "score": 9 },
    { "name": "Physics", "score": 4 },
    { "name": "Physics", "score": 21 },
    { "name": "Physics", "score": 11 },
    { "name": "Physics", "score": 21 },
    { "name": "Physics", "score": 11 },
    { "name": "Physics", "score": 21 },
    { "name": "Physics", "score": 11 },
    { "name": "Physics", "score": 11 },
];
</script>

It's possible to make the code at least twice faster with building the result array manually. Also avoid Array::shift() to mutate an array since write operations are slow.

let filteredArray = subjects.filter(value => value.name == "Chemistry");
let filteredArray2 = new Array(subjects.length);
let j = 0;
for(let i = 0, n = 0; i < subjects.length; i++){
  if(subjects[i].name === 'Chemistry'){
    continue;
  }
  if(j % 3 === 2 && n < filteredArray.length){
    filteredArray2[j++] = filteredArray[n++];
  }
  filteredArray2[j++] = subjects[i];
}
filteredArray2.forEach(item => console.log(JSON.stringify(item)));
<script>
const subjects = [
    { "name": "Math", "score": 32 },
    { "name": "Chemistry", "score": 17 },
    { "name": "English", "score": 17 },
    { "name": "Math", "score": 55 },
    { "name": "Chemistry", "score": 21 },
    { "name": "Chemistry", "score": 75 },
    { "name": "Chemistry", "score": 45 },
    { "name": "Physics", "score": 9 },
    { "name": "Physics", "score": 4 },
    { "name": "Physics", "score": 21 },
    { "name": "Physics", "score": 11 },
    { "name": "Physics", "score": 21 },
    { "name": "Physics", "score": 11 },
    { "name": "Physics", "score": 21 },
    { "name": "Physics", "score": 11 },
    { "name": "Physics", "score": 11 },
];
</script>

And a benchmark:

enter image description here

<script benchmark data-count="3000000">

    const subjects = [
        { "name": "Math", "score": 32 },
        { "name": "Chemistry", "score": 17 },
        { "name": "English", "score": 17 },
        { "name": "Math", "score": 55 },
        { "name": "Chemistry", "score": 21 },
        { "name": "Chemistry", "score": 75 },
        { "name": "Chemistry", "score": 45 },
        { "name": "Physics", "score": 9 },
        { "name": "Physics", "score": 4 },
        { "name": "Physics", "score": 21 },
        { "name": "Physics", "score": 11 },
        { "name": "Physics", "score": 21 },
        { "name": "Physics", "score": 11 },
        { "name": "Physics", "score": 21 },
        { "name": "Physics", "score": 11 },
        { "name": "Physics", "score": 11 },
    ];

    // @benchmark original
    {
    let filteredArray = subjects.filter(value => value.name == "Chemistry");
    let filteredArray2 = subjects.filter(value => value.name != "Chemistry");


    for (let i = 0; i <= filteredArray2.length && filteredArray.length; i++){
      i % 3 === 2 && filteredArray2.splice( i++ , 0, filteredArray.shift());
    }
    filteredArray2;
    }

    // @benchmark optimized
    {
    let filteredArray = subjects.filter(value => value.name == "Chemistry");
    let filteredArray2 = new Array(subjects.length);
    let j = 0;
    for(let i = 0, n = 0; i < subjects.length && n < filteredArray.length; i++){
      if(subjects[i].name === 'Chemistry'){
        continue;
      }
      if(j % 3 === 2 && n < filteredArray.length){
        filteredArray2[j++] = filteredArray[n++];
      }
      filteredArray2[j++] = subjects[i];
    }
    filteredArray2;
    }
    
    // @benchmark moonwave99
    function arrangeBySubject(subjects, subject) {
      const subjectEntries = subjects.filter((x) => x.name === subject);
      const nonSubjectEntries = subjects.filter((x) => x.name !== subject);

      return Array.from({ length: subjects.length }, (_, index) => {
          if (index % 3 === 2 && subjectEntries.length) {
              return subjectEntries.shift();
          }
          return nonSubjectEntries.shift();
      });
    }
    // @run
    arrangeBySubject(subjects, 'Chemistry');


</script>
<script src="https://cdn.jsdelivr.net/gh/silentmantra/benchmark/loader.js"></script>

3
moonwave99 On

If you want to make it more flexible, write a function for it:

function arrangeBySubject(subjects, subject) {
    const subjectEntries = subjects.filter((x) => x.name === subject);
    const nonSubjectEntries = subjects.filter((x) => x.name !== subject);

    return Array.from({ length: subjects.length }, (_, index) => {
        if (index % 3 === 2 && subjectEntries.length) {
            return subjectEntries.shift();
        }
        return nonSubjectEntries.shift();
    });
}

const subjects = [
    { name: "Math", score: 32 },
    { name: "Chemistry", score: 17 },
    { name: "English", score: 17 },
    { name: "Math", score: 55 },
    { name: "Chemistry", score: 21 },
    { name: "Chemistry", score: 75 },
    { name: "Chemistry", score: 45 },
    { name: "Physics", score: 9 },
    { name: "Physics", score: 4 },
    { name: "Physics", score: 21 },
    { name: "Physics", score: 11 },
    { name: "Physics", score: 21 },
    { name: "Physics", score: 11 },
    { name: "Physics", score: 21 },
    { name: "Physics", score: 11 },
    { name: "Physics", score: 11 },
];

const output = arrangeBySubject(subjects, "Chemistry");
console.log(output);