简体   繁体   中英

Sorting an array of objects -> if key comes later in the array, change order

Let's say I have this array of objects:

let arrOfObjs = [
{
    "id": "unique1",
    "parentId": "unique3", // So this one is equal to arrOfObjs[2].id
    "title": "title1"
}, 
{
    "id": "unique2",
    "parentId": "unique3", // This one is also equal to arrOfObjs[2].id
    "title": "title2"
}, 
{
    "id": "unique3",
    "parentId": "",
    "title": "title3"
}
]

The situation is:

  • The id is always unique

  • The parentId is not unique. The parentId is equal to one of the ids in one of the objects

What I want to achieve:

The id should always come earlier than the parentId in the array. In the example above, the first two objects contain 'unique3' (id of the 3d object) as parentId. That shouldn't happen.

So it should be sorted like this:

let arrOfObjs = [
{
    "id": "unique3",
    "parentId": "",
    "title": "title3"
}
{
    "id": "unique2",
    "parentId": "unique3", 
    "title": "title2"
}, 
{
    "id": "unique1",
    "parentId": "unique3",
    "title": "title1"
}
]

So based on the parentId of the object, it should find the id which is equal to the parentId and when the index of the object with that id is higher, that object should come first.

It's a little bit hard to explain but I hope it's clear, let me know if you have any questions

Haven't tried anything yet, no idea how I can achieve this.

try this:

  arrOfObjs.sort((a, b) => {
  let aIndex = arrOfObjs.findIndex(obj => obj.id === a.parentId);
  let bIndex = arrOfObjs.findIndex(obj => obj.id === b.parentId);
  return aIndex - bIndex;
});

You could go from the end and check if the predecessors contians the parent and swap the items.

 const sort = array => { let l = array.length, i = l; while (--i) { let j = i; while (j--) { if (array[j].parentId === array[i].id) { [array[i], array[j]] = [array[j], array[i]]; i = l; break; } } } return array; }; console.log(sort([{ id: "unique1", parentId: "unique3", title: "title1" }, { id: "unique2", parentId: "unique3", title: "title2" }, { id: "unique3", parentId: "", title: "title3" }])); console.log(sort([{ id: 4, parentId: 2 }, { id: 1, parentId: 3 }, { id: 2, parentId: 3 }, { id: 3, parentId: 0 }]));
 .as-console-wrapper { max-height: 100%;important: top; 0; }

The first thing I thought, you simply need to move all items that have no parent on the top of the array. In this case, all 'parents' are declared before their use.

And we can achieve this with a sort function:

arrOfObjs.sort(({ parentId: a }, { parentId: b }) => {
  if (a === '')
    return -1;
  if (b === '')
    return 1;
  return 0;
});

Then I realized that there is a probability that some 'parent' could be a parent item for some other item. Thus sort function doesn't satisfy us.

The solution is next. We iterate over the array and move the item to the tail if there is no item with id that equals its parentId in the predeceased slice of the array.

The trick is to modify the array on-the-fly in the loop. Not sure about the time complexity , perhaps it is θ(n^2).

for (let i = 0, [item] = arrOfObjs; i < arrOfObjs.length; item = arrOfObjs[++i]) {
  if (item.parentId !== '' && arrOfObjs.slice(0, i).every(x => x.id !== item.parentId)) {
      arrOfObjs.push(arrOfObjs.splice(i--, 1)[0]);
  }
}

Be aware that the cross-linking of two items leads to an infinite loop

We could interpret your array of objects as an graph. Objects are the vertices, nonempty parentId means vertices are connected with edge.

Sorting of the objects is possible if, and only if, they represent a forest - if there is a cycle in the graph there is no ordering (except for loop - vertex connected to itself), and if there is no cycle we have a forest.

Algorithm should create a forest from the array, then traverse all trees in BFS order pushing vertices to the result array. If the BFS visits some vertex twice it means there is a cycle.

I think this should be doable in O(n) complexity. I'll try to include some code later.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM