1

I don't know if this is a peculiar case, but the problem is quite simple at hand. Let's say you have an array which defines the order in which your objects must be sorted. Let's say this array is like this one:

var sort = [5, 1, 6];

This array order is arbitrary (the numbers aren't sorted according to any specific rule), and the sorted array of objects must follow the order in which the items in the sort array appear)

And you have an array of objects, like this one:

var obj = [{id: 1}, {id: 6}, {id:5}];

So the result (according to the order in sort and the value of each object's id), should be this:

obj = [{id: 5}, {id: 1}, {id:6}];

I have tried this approach:

var obj = [{id: 1}, {id: 6}, {id:5}];
var sort = [5, 1, 6];

var indexedObjArr = [],
    tmpObj = {};
obj.forEach(function(item) {
    tmpObj[item.id] = item;
    indexedObjArr.push(tmpObj);
    tmpObj = {};
});
obj = [];

//This is where it fails, obj ends up having all undefined entries
indexedObjArr.forEach(function(item, i) {
    // [sort[i]] === undefined, but sort[i] gives an actual number (e.g. 5),
    // and, for instance item[5], gives back the correct item
    obj.push(item[sort[i]]);
});
console.log(obj);

What am I doing wrong in this script? Or can you think of a better approach to the problem?

Thank you.

  • 5
    The `Array` prototype in JS already has a `sort` method that accepts a custom comparator. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort – Dai Apr 12 '15 at 03:00
  • Thanks, but from what I understand, I can only compare according to some sort of order, right? The sort order I want is arbitrary, there isn't a criteria other than the order of the items in the `sort` array. – GreasyDonkey Apr 12 '15 at 03:12
  • possible duplicate of [Sorting an array of JavaScript objects](http://stackoverflow.com/questions/979256/sorting-an-array-of-javascript-objects) – Anik Islam Abhi Apr 12 '15 at 03:13
  • Is the number of items on the list equals the number of items in your sort list? – IndieTech Solutions Apr 12 '15 at 03:20
  • Yeah, the lengths are always equal, and the sort array always contains the ids of each item in the objects array. – GreasyDonkey Apr 12 '15 at 03:23
  • 1
    ez: `obj.sort(function(a,b){ return sort.indexOf(a.id)-sort.indexOf(b.id);})` – dandavis Apr 12 '15 at 03:24

4 Answers4

2

Why not just use the built in sort method for Arrays?

obj.sort(function(a, b) { return sort.indexOf(a.id) - sort.indexOf(b.id) });

var obj = [{id: 1}, {id: 6}, {id: 5}];
var sort = [5, 1, 6];

console.log(obj.sort(function(a, b) {
  return sort.indexOf(a.id) - sort.indexOf(b.id)
}));
Jason Cust
  • 10,743
  • 2
  • 33
  • 45
  • Awesome, so simple... I'm going to use this. I couldn't have come up with that, to be honest, but now that I see it, I think, how couldn't I have come up with that?! – GreasyDonkey Apr 12 '15 at 03:48
0

Flaw in your code: indexedObjArr will look like [ { 1: id1item}, {6: id6item}, {5: id5item} ]

Clearly when you iterate over this array, item object will be one of the items in the array e.g. { 1 : id1item }. Now if you try to do item[sort[i]] i.e. item[5] this will be undefined obviously.

Try this (only corrections... update your code):

var indexedObj = {};//object not array
obj.forEach(function(item) {
    indexedObj[item.id] = item;
});
//now the indexedObj will be { 1 : id1item, 6: id6item,...}

obj = [];

//run the loop in the order you want the sorting i.e. [5, 1, 6]
sort.forEach(function(id) {
    obj.push(indexedObj[id]);//get the item for id as in the sort array
});

console.log(obj);

Another issue with this approach is sort array is assumed to be of the same size as the array. If that's not the case, then special handling will be required. One solution for that could be:

sort.forEach(function(id) {
    obj.push(indexedObj[id]);
    //remove the processed id from indexedObj
    delete indexedObj[id];
});
//now add all the objects left out.
for(var item in indexedObj) {
    obj.push(indexedObj[item]);
}
gp.
  • 8,074
  • 3
  • 38
  • 39
  • Thanks for your effort, this works fine. There's a typo in the final solution, though. `for (var item in indexedObj)`. I'm picking the simpler answer (@Jason Cust), though, since its well, simpler, but I wish I could upvote this... – GreasyDonkey Apr 12 '15 at 03:46
  • No issues... this answer does point out the issue in your code and corrects it as asked in the question. using inbuilt sort function is surely simpler but not sure about cost of calling indexOf everytime in comparator... you should check that too. – gp. Apr 12 '15 at 04:01
  • 1
    Yeah, you were the only one that explained that, thanks for that and for pointing out the performance cost. I just did this jsPerf, and if I did the tests correctly, @VicF answer is apparently the fastest :O http://jsperf.com/specific-sort – GreasyDonkey Apr 12 '15 at 04:35
  • Interesting... with more number of items (say 100), Array.sort performance goes down (worse than your approach)... VicF's approach is stil the fastest. http://jsperf.com/specific-sort/3 – gp. Apr 12 '15 at 09:04
0

One might use a middle-man to fetch the appropriate item from the root source, such as with "findObj" here. There's probably a more elegant way to do this, but a "brute force" approach like this will get you there.

var obj = [{id: 1}, {id: 6}, {id:5}];
var sort = [5, 1, 6];
var result = [];
var findObj = function(id){
    for (var i = 0; i < obj.length; i++){
        if (obj[i].id === id){return obj[i];}
    };
    return null;
};
for (var pos = 0; pos < sort.length; pos++){
    result.push(findObj(sort[pos]));
}
console.log(JSON.stringify(result));

I created a solution here: http://jsfiddle.net/pp3qzowz/ Hope this helps.

Vic F
  • 1,143
  • 1
  • 11
  • 26
0

Here is my solution :) I hope it help !

    var obj = [{id: 1}, {id: 6}, {id:5}];
var sort = [5, 1, 6];

var indexedObjArr = [],
    sortedObj = [];
sort.forEach(function(item) {
    obj.forEach(function(objItem){
        if(objItem.id == item)
            sortedObj.push(objItem);
    });
});
console.log(sortedObj);
Phi Nguyen
  • 3,046
  • 14
  • 26