-5

I have a JS object that has the following snippet:

{
    "foo": "bar",
    "baz": [
        {
            "order": 2,
            "fruit": "banana"
        },
        {
            "order": 1,
            "fruit": "apple"
        },
        {
            "order": 3,
            "fruit": "peach"
        },
    ]
}

My goal is to be able to iterate through all the objects in "baz" but in the order according to their "order" property, not the order as stored in the object. The order of the objects in "baz" can/will change frequently, so sorting them previously or storing them in the order they need to be in is not an option.

A pure JS or jQuery answer is all acceptable.

Felix Jen
  • 154
  • 7
  • sort `baz` array using `Array.sort` – Jaromanda X Aug 08 '17 at 04:58
  • Where are you stuck? What have you tried? What has your research turned up? When you say *"The order of the objects in "baz" can/will change frequently..."* do you mean literally their order in the array, or their `order` property value? – T.J. Crowder Aug 08 '17 at 04:58
  • to display anything in order means sorting it first, even if you do it on the fly. – Jorg Aug 08 '17 at 04:58
  • @Sam: From the question: *"... so **sorting them previously...is not an option**"* – T.J. Crowder Aug 08 '17 at 05:07
  • If sorting them was an option (per your [currently accepted answer](https://stackoverflow.com/a/45559603/157247)), next time, please **don't actively mislead us** by saying it's not an option. – T.J. Crowder Aug 08 '17 at 05:14

3 Answers3

1

Hello you have to sort your object. Just use .sort of the array for that.

Here is a sample:

var obj = {
    "foo": "bar",
    "baz": [
        {
            "order": 2,
            "fruit": "banana"
        },
        {
            "order": 1,
            "fruit": "apple"
        },
        {
            "order": 3,
            "fruit": "peach"
        },
    ]
}

// get property
var arr = obj["baz"];

// may copy array
var counter = arr.length;
var arrCopy = new Array(counter);
while(counter--) {
    arrCopy[counter] = arr[counter];
}

// sort 
arrCopy.sort(function(a, b) { return a.order - b.order});

// iterate it
arrCopy.forEach(function(v) {
  console.log(v.fruit);
})
kpalatzky
  • 1,213
  • 1
  • 11
  • 26
  • From the question: *"...so sorting them previously or storing them in the order they need to be in is not an option."* – T.J. Crowder Aug 08 '17 at 05:01
  • But the only way to do that - Even if you sort it by the iteration - You have to sort it! – kpalatzky Aug 08 '17 at 05:02
  • No, it's not your only option, and again, the OP quite clearly said they couldn't sort `baz`, so telling them to sort `baz` is not useful. – T.J. Crowder Aug 08 '17 at 05:03
  • If it is not possible to store it in the right order or not to sort it, then is there no "better" solution - Every solution would be slower. So this is one of the best solutions. And i do not see why sorting should not be used (Its only written in the question, but its still the best solution) – kpalatzky Aug 08 '17 at 05:08
  • If it is not possible to store it in the right order or not to sort it, then is there no "better" solution - Every solution would be slower. So this is one of the best solutions. And i do not see why sorting should not be used (Its only written in the question, but its still the best solution) – kpalatzky Aug 08 '17 at 05:09
0

You've said

The order of the objects in "baz" can/will change frequently...

If you literally mean just that, and not that their order property changes, you can give yourself another array, separate from baz, that contains the objects in order. Then it doesn't matter what order they're in in baz.

var bazInOrder = theObject.baz.slice();
bazInOrder.sort(function(a, b) {
    return a.order - b.order;
});

If you add an entry to baz, you'll need to add it (in sorted position) to bazInOrder; and similarly if you delete an entry from baz, you'll want to delete it from bazInOrder.


Your other option is to add a property to them pointing to the "next" entry; e.g., overlaying a linked list on the array. That again requires a fair bit of maintenance on add/remove, but not just if their order in baz changes.

theObject.baz.slice()
         .sort(function(a, b) { return a.order - b.order; })
         .forEach(function(entry, index, arr) {
    entry.next = index < arr.length - 1 ? arr[index + 1] : null;
});

then to access them in order requires finding the order = 1 entry, then looping with next:

for (var e = theObject.baz.find(function(e) { return e.order === 1; });
     e;
     e = e.next) {
    // ...use `e`...
}

Again, though, linked lists involve maintenance on add/remove.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • From the question: "...so sorting them previously or storing them in the order they need to be in is not an option." – kpalatzky Aug 08 '17 at 05:11
  • @archos: I'm not sorting `baz`. And the second solution above doesn't use a sorted array at all. – T.J. Crowder Aug 08 '17 at 05:12
  • Yes if you know that the array will/can change while iteration it should be copied first (Butt better use the while loop instead `slice` it is about 10-30% faster). And the second solution is the part *storing them in the order they need* – kpalatzky Aug 08 '17 at 05:18
  • 1
    @archos: Meh, a separate index is far enough of afield of that it seemed reasonable, whereas doing *exactly* what they said they couldn't do isn't. But it doesn't matter, as the question was actively misleading and a complete waste of everyone's time (based on accepting your answer), since it's just a straight-up duplicate. – T.J. Crowder Aug 08 '17 at 05:22
0

I guess you could create an array that has the original index of baz in order of baz[n].order

a bit like

var obj = {
    "foo": "bar",
    "baz": [
        {
            "order": 2,
            "fruit": "banana"
        },
        {
            "order": 1,
            "fruit": "apple"
        },
        {
            "order": 3,
            "fruit": "peach"
        },
    ]
}
var bazIndexOrder = obj.baz
  .map(({order}, index) => ({index, order}))
  .sort(({order:a}, {order:b}) => a - b)
  .map(({index}) => index);

console.log(bazOrder); // [1, 0, 2]

bazOrder.forEach(index => console.log(obj.baz[index].fruit)); // apple, banana, peach

If the ES2015+ code scares you, it's

var bazIndexOrder = obj.baz.map(function (_ref, index) {
  var order = _ref.order;
  return { index: index, order: order };
}).sort(function (_ref2, _ref3) {
  var a = _ref2.order;
  var b = _ref3.order;
  return a - b;
}).map(function (_ref4) {
  var index = _ref4.index;
  return index;
});
Jaromanda X
  • 53,868
  • 5
  • 73
  • 87