Skip to content

Instantly share code, notes, and snippets.

@ecarter
Created December 2, 2011 15:40
Show Gist options
  • Select an option

  • Save ecarter/1423674 to your computer and use it in GitHub Desktop.

Select an option

Save ecarter/1423674 to your computer and use it in GitHub Desktop.
Order an array of objects based on another array order
/**
* Sort array of objects based on another array
*/
function mapOrder (array, order, key) {
array.sort( function (a, b) {
var A = a[key], B = b[key];
if (order.indexOf(A) > order.indexOf(B)) {
return 1;
} else {
return -1;
}
});
return array;
};
/**
* Example:
*/
var item_array, item_order, ordered_array;
item_array = [
{ id: 2, label: 'Two' }
, { id: 3, label: 'Three' }
, { id: 5, label: 'Five' }
, { id: 4, label: 'Four' }
, { id: 1, label: 'One'}
];
item_order = [1,2,3,4,5];
ordered_array = mapOrder(item_array, item_order, 'id');
console.log('Ordered:', JSON.stringify(ordered_array));
@eduardmarcinov

Copy link
Copy Markdown

It possible to order by another array of objects which are different properties? item_array would be same as in your script but ordered_array would be like this [ { catalogId:1}, { catalogId:2}, { catalogId:2 } { catalogId:3 },,{ catalogId:4 },...] and I need to order by catalogId

@anchal20

Copy link
Copy Markdown

@abdekalder: It is possible, just a small tweek when sending the params to mapOrder function. You can refer my fiddle for the approach.
I hope it is helpful.

Thanks,
Anchal

@ismailbayram

Copy link
Copy Markdown

awesome man !

@hibangun

Copy link
Copy Markdown

what if, let's say

item_array = [ 
  { id: 2, label: 'Two' }
, { id: 3, label: 'Three' }
, { id: 5, label: 'Five' }
, { id: 4, label: 'Four' }
, { id: 1, label: 'One'}
, { id: 6, label: 'Six'} // new object that doesn't have in item_order
];

item_order = [1,2,3,4,5];

The result would be

[ 
  { id: 6, label: 'Six'} // new object is on top
, { id: 1, label: 'One' }
, { id: 2, label: 'Two' }
, { id: 3, label: 'Three' }
, { id: 4, label: 'Four' }
, { id: 5, label: 'Five'}
];

how can we move object that not found in item_order eg:{id: 6, label: 'Six') to be on the bottom instead?

@ecarter @gy0857478 @abdekalder @anchal20 @ismailbayram

@rikardocorp

rikardocorp commented Nov 20, 2018

Copy link
Copy Markdown

if you want to insert new elements at the end of the array

Change the line 11:
if (order.indexOf(A) > order.indexOf(B))
by:
if (order.indexOf(A) > order.indexOf(B) || order.indexOf(A) === -1 || order.indexOf(B) === -1)

@ecarter @hibangun

@PrnOnii

PrnOnii commented Mar 7, 2019

Copy link
Copy Markdown

@rikardocorp
I think you made a slight mistake.
Your change does not add new elements at the END of the array but rather at the BEGINNING.

If you really want to add at the END of the array, you change lines 11 to 15 :
if (order.indexOf(A) > order.indexOf(B)) {
return 1;
} else {
return -1;
}
To this :
if (order.indexOf(A) < order.indexOf(B) || order.indexOf(A) === -1 || order.indexOf(B) === -1) {
return -1;
} else {
return 1;
}

@JD-V

JD-V commented Mar 11, 2019

Copy link
Copy Markdown

@rikardocorp
None of above comments were useful in my case

so I tweaked the logic a bit like this and it worked!!
`let indA = sortedOrder.indexOf(A)
let indB = sortedOrder.indexOf(B)

    if (indA == -1 )
        indA = sortedOrder.length-1
    if( indB == -1)
        indB = sortedOrder.length-1

    if (indA < indB ) {
        return -1;
    } else if (indA > indB) {
        return 1;
    }
    return 0;`

@MontoyaAndres

Copy link
Copy Markdown

It works perfectly with uuid's 😄

@fa-901

fa-901 commented Mar 28, 2019

Copy link
Copy Markdown

Thank you. You are my saviour

@pmsoltani

pmsoltani commented Oct 25, 2019

Copy link
Copy Markdown

Just what I was looking for. Thanks. I made a new version from it:

const mapOrder = (array, order, key) => {
  array.sort((a, b) => {
    const A = a[key];
    const B = b[key];
    return order.indexOf(A) > order.indexOf(B) ? 1 : -1;
  });
  return array;
};

@rexl2018

rexl2018 commented Feb 14, 2020

Copy link
Copy Markdown

not good in time complexity as O(n*n*log(n)).

Here is another version of O(n):

function mapOrder(array, order, key) {
    var map = new Map();
    var index = 0;
    var tmp;
    if(!array || !order || array.length!==order.length)
      return array;
    array.forEach(function(it) {
        map.set(it[key], index++);
    });
    order.forEach(function(it) {
      if(map.get(it) === undefined) return array;
    });
    index--;
    for (; index >= 0; index--) {
        if (array[index][key] !== order[index]) {
            tmp = array[index];
            array[index] = array[map.get(order[index])];
            array[map.get(order[index])] = tmp;
            map.set(tmp[key], map.get(order[index]));
        }
    }
    return array;
}

Correct me if I were wrong.

@lokhmakov

Copy link
Copy Markdown
function mapOrder(a, order, key) {
  const map = order.reduce((r, v, i) => ((r[v] = i), r), {})
  return a.sort((a, b) => map[a[key]] - map[b[key]])
}

const source = [
  {id: 2, label: 'Two'},
  {id: 3, label: 'Three'},
  {id: 5, label: 'Five'},
  {id: 4, label: 'Four'},
  {id: 1, label: 'One'},
]

const order = [1, 2, 3, 4, 5]
const result = mapOrder(source, order, `id`)

@PaulSanchez12

Copy link
Copy Markdown

Just what I was looking for!
Thank you.

@binfask

binfask commented Jun 9, 2020

Copy link
Copy Markdown

Thank you so much i was looking for this

@sandy912

Copy link
Copy Markdown

Thank you

@thegirishagarwal

Copy link
Copy Markdown

Can you sort Order Array according to source id ?

@ssuess

ssuess commented Sep 15, 2020

Copy link
Copy Markdown
mapOrder (array, myorder, key) {
     var order = myorder.reduce((r, k, i) => (r[k] = i + 1, r), {})
     const theSort = array.sort((a, b) => (order[a[key]] || Infinity) - (order[b[key]] || Infinity))
     return theSort
   },

This will sort based on array and key given and if there are more items in the sorted array than in the myorder array put those unsorted at the end

@vaartio

vaartio commented Nov 27, 2020

Copy link
Copy Markdown

How about implementing it as a compare function?

const mapOrder = (order, key) => (a, b) => order.indexOf(a[key]) > order.indexOf(b[key]) ? 1 : -1;

This way, you retain the flexibility that Array’s built-in methods provide.

const item_array = [ 
  { id: 2, label: 'Two' },
  { id: 3, label: 'Three' },
  { id: 5, label: 'Five' },
  { id: 4, label: 'Four' },
  { id: 1, label: 'One'},
];
const item_order = [1,2,3,4,5];

item_array
  .filter(item => item.id > 2)
  .sort(mapOrder(item_order, 'id'))
  .pop()

@Sa-meer

Sa-meer commented Jan 13, 2021

Copy link
Copy Markdown

Thanks a lot this is exactly what I needed.

@NehaAkashDeo

Copy link
Copy Markdown

Thanks a lot for your help.

@kkoo95

kkoo95 commented May 8, 2021

Copy link
Copy Markdown

If you want to always add new items at the end, THIS IS THE ONE:

let indA = sortedOrder.indexOf(A);
let indB = sortedOrder.indexOf(B);

if (indA == -1) {
  indA = sortedOrder.length;
}
if (indB == -1) {
  indB = sortedOrder.length;
}

return indA - indB;

@linuxhackr

Copy link
Copy Markdown

Finally I got what I was looking for. Thanks man.

@linuxhackr

Copy link
Copy Markdown

How about implementing it as a compare function?

const mapOrder = (order, key) => (a, b) => order.indexOf(a[key]) > order.indexOf(b[key]) ? 1 : -1;

This way, you retain the flexibility that Array’s built-in methods provide.

const item_array = [ 
  { id: 2, label: 'Two' },
  { id: 3, label: 'Three' },
  { id: 5, label: 'Five' },
  { id: 4, label: 'Four' },
  { id: 1, label: 'One'},
];
const item_order = [1,2,3,4,5];

item_array
  .filter(item => item.id > 2)
  .sort(mapOrder(item_order, 'id'))
  .pop()

Thanks to make it easy

@LemonyPie

LemonyPie commented Jul 1, 2021

Copy link
Copy Markdown

I think it is possible to just subtract indexes

function mapOrder(order, key) {
  return function(a, b) {
    return order.indexOf(a[key]) - order.indexOf(b[key]) // ascending order
  }
}

e.g.

function sortOrder(order) {
    return function(a, b) {
    return order.indexOf(a) - order.indexOf(b)
    }
}

const arr = ['a', 'b', 'c', 'b', 'c', 'a']
const order = ['a', 'b', 'c']

[...arr].sort(sortOrder(order)) // ["a", "a", "b", "b", "c", "c"]

@kkoo95

kkoo95 commented Jul 5, 2021

Copy link
Copy Markdown

@ArtemeeSenin it doesn't play well with non-existant values. At least for my use case.

with an array like this ['e', 'a', 'b', 'g', 'c', 'h', 'b', null, 'c', 'i', 'a', 'd'],
it gives ['e', 'g', 'h', null, 'i', 'd', 'a', 'a', 'b', 'b', 'c', 'c']

But I would expect to have all unknown stuff at the end, sorted ascendingly. but here it just stack them off in front

@RatherLogical

RatherLogical commented Aug 19, 2021

Copy link
Copy Markdown

I made this based off of the OP.
This will sort according to your specified order. It will also sort and add unknown items to the end (a feature the original should've included).

/**
 * Order an array of objects by another array.
 * @param  {array} array The array of objects to sort.
 * @param  {array} order The array of property names to order the objects by.
 * @param  {string} property The property name to use as a sorting target.
 */
function mapOrder(array, order, property) {
    let ordered = [], unordered = [];

    // Iterate over each item in the supplied array of objects, separating ordered and unordered objects into their own arrays.
    array.forEach((item) => {
    if (order.indexOf(item[property]) === -1) {
        unordered.push(item);
    } else {
        ordered.push(item);
    }
    });

    // Sort the ordered array.
    ordered.sort((a, b) => {
    a = a[property], b = b[property];

    if (order.indexOf(a) < order.indexOf(b)) {
        return -1;
    } else {
        return 1;
    }
    });

    // Sort the unordered array.
    unordered.sort((a, b) => {
    a = a[property], b = b[property];

    if (a < b) {
        return -1;
    } else if (a > b) {
        return 1;
    } else {
        return 0;
    }
    });

    // Append the sorted, non-ordered array to the sorted, ordered array.
    ordered.push(...unordered);

    return ordered;
}

@TheOneWayTruth

TheOneWayTruth commented Oct 8, 2021

Copy link
Copy Markdown

why not use

function mapOrder(array, order, key) {
    return array.sort((a, b) => 
        order.indexOf(a[key]) > order.indexOf(b[key]) ? 1 : -1
    );
}

@lokhmakov

lokhmakov commented Oct 9, 2021

Copy link
Copy Markdown

why not use

export function mapOrder(array, order, key) {
    array.sort((a, b) => {
        order.indexOf(a[key]) > order.indexOf(b[key]) ? 1 : -1;
    });
    return array;
}

@TheOneWayTruth u just copy author solution and forgot about return of order comparison. U solution does not work.

And u can return result of array.sort without last return array.

const mapOrder = (array, order, key) =>
  array.sort((a, b) => order.indexOf(a[key]) > order.indexOf(b[key]) ? 1 : -1)

But it still topic starter solution.

@prakashmallow

Copy link
Copy Markdown

Finally I got what I was looking for. Thanks @kkoo95

@sujinleeme

sujinleeme commented Mar 20, 2023

Copy link
Copy Markdown

Here is the code If you want to move unmated rest of items at the end of array.

export const mapOrder = <T>(array: T[], order: any[], key: keyof T) => {
  return array.sort((a, b) => {
    let weightA = 0;
    let weightB = 0;

    if (!order.includes(a[key])) {
      weightA += 100;
    }

    if (!order.includes(b[key])) {
      weightB += 100;
    }

    return order.indexOf(a[key]) + weightA - (order.indexOf(b[key]) + weightB);
  });
};

Test Case

 const item_array = [
    { id: 2, label: "Two" },
    { id: 3, label: "Three" },
    { id: 5, label: "Five" },
    { id: 4, label: "Four" },
    { id: 1, label: "One" }
  ];

  const item_order = [1, 5];

  const ordered_array = mapOrder(item_array, item_order, "id");
  
console.log("Ordered:", JSON.stringify(ordered_array));
// Ordered: [{"id":1,"label":"One"},{"id":5,"label":"Five"},{"id":2,"label":"Two"},{"id":3,"label":"Three"},{"id":4,"label":"Four"}] 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment