根据另一个 id 数组对对象数组进行排序
Posted
技术标签:
【中文标题】根据另一个 id 数组对对象数组进行排序【英文标题】:Sort an array of objects based on another array of ids 【发布时间】:2016-06-02 23:40:04 【问题描述】:我有 2 个数组
a = [2,3,1,4]
b = [id: 1, id: 2, id: 3, id: 4]
如何根据a
对b
进行排序?我想要的输出是
c = [id: 2, id: 3, id: 1, id: 4]
我更喜欢使用 Ramda 或常规 JS。
【问题讨论】:
真正的b
对象是否包含除id
之外的任何其他内容?
javascript - sort array based on another array的可能重复
【参考方案1】:
您可以为 JavaScript 的 Array#sort
方法提供自定义比较函数。
使用自定义比较功能保证排序顺序:
var sortOrder = [2,3,1,4],
items = [id: 1, id: 2, id: 3, id: 4];
items.sort(function (a, b)
return sortOrder.indexOf(a.id) - sortOrder.indexOf(b.id);
);
MDN:
如果 compareFunction(a, b) 返回小于 0,则将 a 排序到低于 b 的索引(即 a 排在第一位)。 如果 compareFunction(a, b) 返回 0,则 a 和 b 彼此保持不变,但对所有不同元素进行排序。注意:ECMAscript 标准不保证这种行为,因此,并非所有浏览器(例如,至少可以追溯到 2003 年的 Mozilla 版本)都尊重这一点。 如果 compareFunction(a, b) 返回大于 0,则将 b 排序到低于 a 的索引(即 b 排在第一位)。
Hitmands 对上述解决方案做出了非常中肯的评论:
这种方法是 O(n2),并且会在大型列表中导致性能问题。最好先构建字典,使其保持 O(n)
因此,上述解决方案最终可能无法满足您在大输入时所需的速度。
实施 Hitmands 的建议:
let sortOrder = [2,3,1,4],
items = [id: 1, id: 2, id: 3, id: 4];
const itemPositions = ;
for (const [index, id] of sortOrder.entries())
itemPositions[id] = index;
items.sort((a, b) => itemPositions[a.id] - itemPositions[b.id]);
【讨论】:
【参考方案2】:Ramda 非常适合这类问题。
在数据量小的地方,我们可以使用简单的reduce函数,以及indexOfhelper。
// match id of object to required index and insert
var sortInsert = function (acc, cur)
var toIdx = R.indexOf(cur.id, a);
acc[toIdx] = cur;
return acc;
;
// point-free sort function created
var sort = R.reduce(sortInsert, []);
// execute it now, or later as required
sort(b);
// [ id: 2 , id: 3 , id: 1 , id: 4 ]
这适用于小型(ish)数据集,但每次通过归约迭代的indexOf 操作对于大型数据集效率低下。
我们可以通过从另一边解决问题来解决这个问题,让我们使用groupBy 按对象的 id 对我们的对象进行分组,从而创建字典查找(好多了!)。然后我们可以简单地map 覆盖所需的索引并将它们转换为该位置对应的对象。
这是使用这种方法的解决方案:
var groupById = R.groupBy(R.prop('id'), b);
var sort = R.map(function (id)
return groupById[id][0];
);
sort(a);
// [ id: 2 , id: 3 , id: 1 , id: 4 ]
最后,这又是一个解决方案,非常简洁:
R.sortBy(R.pipe(R.prop('id'), R.indexOf(R.__, a)))(b);
// [ id: 2 , id: 3 , id: 1 , id: 4 ]
我喜欢使用Ramda 组合具有行为的函数并将算法与其作用的数据分开的方式。您最终会得到非常易读且易于维护的代码。
【讨论】:
你能让sortInsert
成为一个纯函数吗? :)
我不认为 Ramda 在这里大放异彩,我认为这些解决方案的可读性不如纯 JS 替代方案...【参考方案3】:
使用 ES6 地图和查找
const a = [2,3,1,4];
const b = [id: 1, id: 2, id: 3, id: 4];
// map over a, find it in b and return
const c = a.map((i) => b.find((j) => j.id === i));
【讨论】:
【参考方案4】:您可以根据a
创建c
,而无需使用b
:
var a = [2,3,1,4];
var c = [];
for(var i = 0; i < a.length; i++)
c.append(id:a[i]);
希望这会有所帮助!
【讨论】:
可能不完全是问题所要求的,但仍然是跳出框框思考的有用视角【参考方案5】:或者可能更简单
b.sort(function(obj1,obj2)
return a.indexOf(obj1.id) > a.indexOf(obj2.id)
);
【讨论】:
【参考方案6】:此解决方案使用 Array#sort
和辅助对象 c
作为索引。
"1": 2,
"2": 0,
"3": 1,
"4": 3
var a = [2, 3, 1, 4],
b = [ id: 1 , id: 2 , id: 3 , id: 4 ],
c = a.reduce(function (r, a, i)
r[a] = i;
return r;
, );
b.sort(function (x, y)
return c[x.id] - c[y.id];
);
document.write('<pre>' + JSON.stringify(b, 0, 4) + '</pre>');
对于更大的对象,我建议使用Sorting with map。
【讨论】:
【参考方案7】:使用 Ramda,您必须通过 mapObjIndexed 函数将 b 中的对象映射到它们的索引,然后搜索 a 中的值。你可以试试here。
var a = [2,3,1,4];
var b = [id: 1, id: 2, id: 3, id: 4]
R.find( R.propEq('id', a[0]), b )
R.values(R.mapObjIndexed( (num, key, obj) => R.find( R.propEq('id', a[key]), b ) , b))
【讨论】:
【参考方案8】:很好的解决方案。只是留在这里以备将来使用:)
const sortBy = (array, values, key = 'id') => ((map) => values.reduce((a,i) =>
a.push(map[i]) && a,[]))(array.reduce((a,i) => (a[i[key]] = i) && a, ));
用法
a = [2,3,1,4]
b = [id: 1, id: 2, id: 3, id: 4]
sortBy(b, a) // [id: 2, id: 3, id: 1, id:` 4]
【讨论】:
【参考方案9】:纯javascript,使用Array
(ES2015
标准)的一些方法
var a = [2,3,1,4];
var b = [id: 1, id: 2, id: 3, id: 4];
var c = [];
a.forEach(el => c.push(b.find(e => e.id == el)));
document.write(JSON.stringify(c, 0, 2));
【讨论】:
【参考方案10】:这是一个使用 Ramda 的替代结构,它可能更简洁一些,并且这些功能很容易重新用于其他用途。
const indexBy, prop, map, flip = R
const a = [2,3,1,4]
const b = [id: 1, id: 2, id: 3, id: 4]
const toIndexById = indexBy(prop('id'))
const findIndexIn = flip(prop)
const c = map(findIndexIn(toIndexById(b)), a)
console.log(c)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>
【讨论】:
【参考方案11】:我将迭代 arcseldon 的解决方案。
这里的正确方法是翻转问题,而不是
"根据b
给出的顺序对a
进行排序",
我宁愿“用来自a
的数据丰富b
”
这使您可以将此函数的时间复杂度保持在O(n)
,
而不是把它带到O(n2)
。
const pickById = R.pipe(
R.indexBy(R.prop('id')),
R.flip(R.prop),
);
const enrich = R.useWith(R.map, [pickById, R.identity]);
// =====
const data = [2, 3, 1, 4];
const source = [ id: 1 , id: 2 , id: 3 , id: 4 ];
console.log(
enrich(source, data),
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous"></script>
【讨论】:
以上是关于根据另一个 id 数组对对象数组进行排序的主要内容,如果未能解决你的问题,请参考以下文章
编写一个函数对对象数组进行排序(通过使用另一个对象来指定排序路径和顺序)