user7597670
user7597670

Reputation:

rearrange objects in array javascript

I have an array of objects being returned from an API.

let arr = [
  {name: 'Blur'},
  {name: 'The Beatles'},
  {name: 'Oasis'},
  {name: 'Arctic Monkeys'},
  {name: 'Elvis'}
];

I always want Arctic Monkeys to come one before Oasis and leave the other items as they are. Sometimes the API will return them in the correct order, but in some instances they will be the wrong way around like the above.

Can anyone advise of the best way to do this?

I know I can do something like this but feel there must be a better way.

for(let i = 0; i < arr.length; i++){
  if(arr[i].name == 'Arctic Monkeys' && arr[i - 1].name === 'Oasis' ){
    let oasisObj = arr[i];
    arr[i - 1] = arr[i];
    arr[i] = oasisObj;
  }
}

Or perhaps I should not mutate this array and create a whole new array ?

Upvotes: 1

Views: 2256

Answers (4)

Gershom Maes
Gershom Maes

Reputation: 8170

Here's a function which takes an array, and two names of the items within that array. It then ensures that the 1st named item appears directly before the 2nd named item!

var ensureAbove = function(arr, above, below) {
    // Find the indices of the above and below items
    var aboveInd = -1;
    var belowInd = -1;
    for (var i = 0; i < arr.length; i++) {
         if (arr[i].name === above) aboveInd = i;
         if (arr[i].name === below) belowInd = i;

         // If we've found both indices we can stop looking
         if (aboveInd > -1 && belowInd > -1) break;
    }

    // Now ensure that the item at index `aboveInd` comes before
    // index `belowInd`
    var loInd = Math.min(belowInd, aboveInd);
    var hiInd = Math.max(belowInd, aboveInd);

    // All the items before `aboveInd` and `belowInd`
    var itemsBefore = arr.slice(0, loInd);

    // All the items after both `aboveInd` and `belowInd`
    var itemsAfter = arr.slice(hiInd + 1);

    // All the items between `aboveInd` and `belowInd`
    var itemsBetween = arr.slice(loInd + 1, hiInd);

    /*
    Ok here's the tactical bit. We can definitely add all the
    `itemsBefore` as the very first thing, and we can definitely
    add all the `itemsAfter` as the very last thing. This middle
    is trickier - we either want to add the above and below items and then
    the middle items, OR we add the middle items first and then the above or
    below items.
    */

    var result = itemsBefore;

    if (belowInd < aboveInd) {
        result.push(arr[aboveInd]);
        result.push(arr[belowInd]);
    }

    result = result.concat(itemsBetween);

    if (belowInd > aboveInd) {
        result.push(arr[aboveInd]);
        result.push(arr[belowInd]);
    }

    result = result.concat(itemsAfter);

    return result;
};

Now apply this code to your array:

let arr = [
    {name: 'Blur'},
    {name: 'The Beatles'},
    {name: 'Oasis'},
    {name: 'Arctic Monkeys'},
    {name: 'Elvis'}
];
let orderedArr = ensureAbove(arr, 'Arctic Monkeys', 'Oasis');

Note that depending on your situation, it may be more efficient to fix your api.

Upvotes: 1

ibrahim mahrir
ibrahim mahrir

Reputation: 31712

let arr = [
  {name: 'Blur'},
  {name: 'The Beatles'},
  {name: 'Oasis'},
  {name: 'Elvis'},
  {name: 'Arctic Monkeys'},
];

// First: look for their indexes
let oasis = -1, monkey = -1;                       // assuming the indexes are -1
for(let i = 0; i < arr.length && (oasis === -1 || monkey === -1); i++) { // the loop will end when they're both found or the end of the array is reached
  if(arr[i].name === "Arctic Monkeys") monkey = i; // if this elemenet is the monkey then assign the current index i to monkey
  else if(arr[i].name === "Oasis") oasis = i;      // ... same for oasis
}

// Second: if they're not in the right order, change the position of monkey
if(oasis !== -1 && monkey !== -1 && monkey !== oasis - 1) { // if we found them both and if monkey isn't right before oasis, then 
  monkey = arr.splice(monkey, 1) [0];             // cut monkey out (using splice 1)
  arr.splice(oasis, 0, monkey);                   // insert it right before oasis (using splice 0 monkey)
}

console.log(arr);

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386776

You could use an object and check if both names are in the object.

But Array#sort might no produce stable results, depending on the implemented sort algorithm.

let arr = [{ name: 'Blur' }, { name: 'The Beatles' }, { name: 'Oasis' }, { name: 'Arctic Monkeys' }, { name: 'Elvis' }],
order = { 'Arctic Monkeys': 1, Oasis: 2 };

arr.sort((a, b) => (b.name in order) && (a.name in order) ? order[a.name] - order[b.name] : 0);

console.log(arr);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 0

E. Sundin
E. Sundin

Reputation: 4180

The best way I believe is to adjust this on the server side with some specific sorting rule. To just fix the specific scenario on the client you might do.

let arr = [
  {name: 'Blur'},
  {name: 'The Beatles'},
  {name: 'Oasis'},
  {name: 'Arctic Monkeys'},
  {name: 'Elvis'}
]

console.log(arr.map(o => o.name))

let iO = arr.findIndex(o => o.name === 'Oasis')
let iA = arr.findIndex(o => o.name === 'Arctic Monkeys')

if ((iA !== -1 && iO !== -1) && iA > iO) {
  let o = arr[iO]
  let a = arr[iA]
  arr[iO] = a
  arr[iA] = o
}

console.log(arr.map(o => o.name))

Upvotes: 0

Related Questions