我正在使用API返回一个对象数组。每个对象都有一个ID,它还指定哪个对象在它自身之前和之后具有beforeId
和afterId
属性。如果它是列表中的第一个对象,则beforeId
为空(因为列表中没有任何内容),如果它是列表中的最后一个对象,那么{afterId
1}}为空。
来自API的示例响应:
var myUnorderedObjects = [
{ id: 896, beforeId: 392, afterId: 955 },
{ id: 955, beforeId: 896, afterId: null }
{ id: 451, beforeId: null, afterId: 392 },
{ id: 392, beforeId: 451, afterId: 896 },
]
有序数组的示例:
var myOrderedObjects = [
{ id: 451, beforeId: null, afterId: 392 },
{ id: 392, beforeId: 451, afterId: 896 },
{ id: 896, beforeId: 392, afterId: 955 },
{ id: 955, beforeId: 896, afterId: null }
]
订购这些物品的最佳方法是什么?目前,我的应用程序实现了自己的排序逻辑,该逻辑与API分开。我想让它们保持一致。到目前为止,我的一般方法是找到第一个对象,找出null
上设置了beforeId
的对象,然后查找并查找afterId
中指定的每个对象,但这只是好像有点......垃圾。
我的应用程序使用Underscore.js,所以我很高兴能够使用这个库的任何答案。
修改
我在两个答案之间进行了快速的性能测试。 Nina Scholz的答案是最快的,也是我甚至没有考虑过的方法。 http://jsperf.com/order-objects-with-beforeid-and-afterid
答案 0 :(得分:0)
由于id只是任意数字,因此无法从它们生成可能用于排序的任何类型的对象索引。从beforeId
null开始并在afterId
字段后面是一个合理的解决方案。如果这是从API获取数据的方式,那么您订购对象的方法没有任何问题。
答案 1 :(得分:0)
这样就可以了:
function findElementWithPropertyValue (array, property, value) {
var index = array.map(function (element) {
return element[property];
}).indexOf(value);
return array[index];
}
function reorderResults (unsorted) {
var sorted = [];
sorted.push(findElementWithPropertyValue(unsorted, 'beforeId', null));
while (sorted.length < unsorted.length) {
sorted.push(findElementWithPropertyValue(unsorted, 'beforeId', sorted[sorted.length - 1].id));
}
return sorted
}
var myUnorderedObjects = [
{ id: 896, beforeId: 392, afterId: 955 },
{ id: 955, beforeId: 896, afterId: null },
{ id: 451, beforeId: null, afterId: 392 },
{ id: 392, beforeId: 451, afterId: 893 }
],
myOrderedObjects = reorderResults(myUnorderedObjects);
console.log(myOrderedObjects);
答案 2 :(得分:0)
如果所有项目都已链接,则此解决方案有效。
function strange(a, b) {
if (
a.beforeId === null ||
b.afterId === null ||
a.id === b.beforeId ||
a.afterId === b.id
) {
return -1;
}
if (
b.beforeId === null ||
a.afterId === null ||
a.id === b.afterId ||
a.beforeId === b.id
) {
return 1;
}
return 0;
}
var unsorted1 = [
{ id: 896, beforeId: 392, afterId: 955 },
{ id: 955, beforeId: 896, afterId: null },
{ id: 451, beforeId: null, afterId: 392 },
{ id: 392, beforeId: 451, afterId: 893 },
],
unsorted2 = [
{ id: 3, beforeId: 2, afterId: 4 },
{ id: 4, beforeId: 3, afterId: null },
{ id: 0, beforeId: null, afterId: 1 },
{ id: 2, beforeId: 1, afterId: 3 },
{ id: 1, beforeId: 0, afterId: 2 }
],
sorted1 = unsorted1.slice().sort(strange),
sorted2 = unsorted2.slice().sort(strange);
document.write('<pre>' + JSON.stringify(sorted1, 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(sorted2, 0, 4) + '</pre>');
更新
Yury Tarabanko向我指出了Chrome 46的一个问题,他是对的,前一版本并不顺利。所以这里有一个更新版本,它使用散列表和sort函数的递归调用。
function sort(array) {
function s(a, b) {
if (a.beforeId === null || b.afterId === null || a.id === b.beforeId || a.afterId === b.id) {
return -1;
}
if (b.beforeId === null || a.afterId === null || a.id === b.afterId || a.beforeId === b.id) {
return 1;
}
return s(o[a.beforeId], b) || s(a, o[b.afterId]) || 0;
}
var o = {};
array = array.slice();
array.forEach(function (a) {
o[a.id] = a;
});
array.sort(s);
return array;
}
var unsorted1 = [
{ id: 896, beforeId: 392, afterId: 955 },
{ id: 955, beforeId: 896, afterId: null },
{ id: 451, beforeId: null, afterId: 392 },
{ id: 392, beforeId: 451, afterId: 896 },
],
unsorted2 = [
{ id: 3, beforeId: 2, afterId: 4 },
{ id: 4, beforeId: 3, afterId: null },
{ id: 0, beforeId: null, afterId: 1 },
{ id: 2, beforeId: 1, afterId: 3 },
{ id: 1, beforeId: 0, afterId: 2 }
],
unsorted3 = [
{ id: 7, beforeId: 6, afterId: 8 },
{ id: 11, beforeId: 10, afterId: null },
{ id: 0, beforeId: null, afterId: 1 },
{ id: 1, beforeId: 0, afterId: 2 },
{ id: 4, beforeId: 3, afterId: 5 },
{ id: 8, beforeId: 7, afterId: 9 },
{ id: 2, beforeId: 1, afterId: 3 },
{ id: 9, beforeId: 8, afterId: 10 },
{ id: 10, beforeId: 9, afterId: 11 },
{ id: 3, beforeId: 2, afterId: 4 },
{ id: 5, beforeId: 4, afterId: 6 },
{ id: 6, beforeId: 5, afterId: 7 },
];
document.write('<pre>' + JSON.stringify(sort(unsorted1), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(sort(unsorted2), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(sort(unsorted3), 0, 4) + '</pre>');
更新2:
我将使用什么:哈希表,开始指针然后重新组装数组。
function chain(array) {
var o = {}, pointer;
array.forEach(function (a) {
o[a.id] = a;
if (a.beforeId === null) {
pointer = a.id;
}
});
array = [];
do {
array.push(o[pointer]);
pointer = o[pointer].afterId;
} while (pointer !== null);
return array;
}
var unsorted1 = [
{ id: 896, beforeId: 392, afterId: 955 },
{ id: 955, beforeId: 896, afterId: null },
{ id: 451, beforeId: null, afterId: 392 },
{ id: 392, beforeId: 451, afterId: 896 },
],
unsorted2 = [
{ id: 3, beforeId: 2, afterId: 4 },
{ id: 4, beforeId: 3, afterId: null },
{ id: 0, beforeId: null, afterId: 1 },
{ id: 2, beforeId: 1, afterId: 3 },
{ id: 1, beforeId: 0, afterId: 2 }
],
unsorted3 = [
{ id: 7, beforeId: 6, afterId: 8 },
{ id: 11, beforeId: 10, afterId: null },
{ id: 0, beforeId: null, afterId: 1 },
{ id: 1, beforeId: 0, afterId: 2 },
{ id: 4, beforeId: 3, afterId: 5 },
{ id: 8, beforeId: 7, afterId: 9 },
{ id: 2, beforeId: 1, afterId: 3 },
{ id: 9, beforeId: 8, afterId: 10 },
{ id: 10, beforeId: 9, afterId: 11 },
{ id: 3, beforeId: 2, afterId: 4 },
{ id: 5, beforeId: 4, afterId: 6 },
{ id: 6, beforeId: 5, afterId: 7 },
];
document.write('<pre>' + JSON.stringify(chain(unsorted1), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(chain(unsorted2), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(chain(unsorted3), 0, 4) + '</pre>');
答案 3 :(得分:0)
正如我在评论中写的那样@Nina的答案是错的。给定函数无法比较2个非兄弟项目,并且返回0不是此处的选项。
第一个不正确的结果来自长度为5的数组。
var shuffled = [
{"id":3,"beforeId":2,"afterId":4},
{"id":4,"beforeId":3,"afterId":null},
{"id":0,"beforeId":null,"afterId":1},
{"id":2,"beforeId":1,"afterId":3},
{"id":1,"beforeId":0,"afterId":2}
];
分类到
[
{"id": 0, "beforeId": null, "afterId": 1},
{"id": 2, "beforeId": 1, "afterId": 3},
{"id": 3, "beforeId": 2, "afterId": 4},
{"id": 1, "beforeId": 0, "afterId": 2},
{"id": 4, "beforeId": 3,"afterId": null}
]
实际上@ Jon的解决方案是正确的。他的幼稚实现可能会优化为O(n)
而不是O(n^2)
。除非您的列表中有数百个项目,否则您不应该担心 micro 优化。 Perf
/**
* Using for loops for *micro* optimization and explicit O(n) estimates.
* http://jsperf.com/order-objects-with-beforeid-and-afterid/2
*/
function linkSort(array) {
var head,
cache = {},
i,
len = array.length,
linked = new Array(len),
item;
//indexing
for(i = 0; i < len; i++) { //O(n) index and find head
item = array[i];
if(item.beforeId === null) { //is head
head = item;
}
else { //index by id
cache[item.id] = item;
}
}
//linking
linked[0] = head;
for(i = 1; i < len; i++) { //O(n) construct linked list
linked[i] = head = cache[head.afterId]; //Lookup is O(1) unlike map+indexOf
}
return linked;
}