Skip to content

Instantly share code, notes, and snippets.

@ecarter
Created December 2, 2011 15:40
Show Gist options
  • Save ecarter/1423674 to your computer and use it in GitHub Desktop.
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));
@linuxhackr
Copy link

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
Copy link

LemonyPie commented Jul 1, 2021

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
Copy link

kkoo95 commented Jul 5, 2021

@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
Copy link

RatherLogical commented Aug 19, 2021

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
Copy link

TheOneWayTruth commented Oct 8, 2021

why not use

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

@lokhmakov
Copy link

lokhmakov commented Oct 9, 2021

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

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

@sujinleeme
Copy link

sujinleeme commented Mar 20, 2023

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