繁体   English   中英

根据另一个 id 数组对对象数组进行排序

[英]Sort an array of objects based on another array of ids

我有 2 arrays

a = [2,3,1,4]
b = [{id: 1}, {id: 2}, {id: 3}, {id: 4}]

如何根据ab进行排序? 我想要的 output 是

c = [{id: 2}, {id: 3}, {id: 1}, {id: 4}]

我更愿意使用 Ramda 或常规 JS。

您可以为 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]);

Ramda非常适合这类问题。

在数据量较小的情况下,我们可以使用一个简单的reduce函数和indexOf助手。

// 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 对对象进行分组,从而创建字典查找(好多了!)。 然后我们可以简单地映射所需的索引并将它们转换为该位置的相应对象。

这是使用这种方法的解决方案:

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作用的数据分开的方式。 您最终会得到非常易读且易于维护的代码。

使用 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));

您可以根据a创建c ,而无需使用b

var a = [2,3,1,4];
var c = [];

for(var i = 0; i < a.length; i++){

    c.append({id:a[i]);

}

希望这可以帮助!

此解决方案使用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 与 map一起使用。

或者可能更简单

b.sort(function(obj1,obj2){
   return a.indexOf(obj1.id) > a.indexOf(obj2.id)
});

使用 Ramda,您必须通过mapObjIndexed函数将b中的对象映射到它们的索引,然后搜索a中的值。 你可以在这里试试。

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))

很好的解决方案。 只是留在这里以备将来使用:)

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}]

您可以使用Array对象的方法:

 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))); console.log(JSON.stringify(c, 0, 2));

这是一个使用 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>

我将迭代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>

只需您可以使用Reduce

 var a = [2, 3, 1, 4]; var c = a.reduce((res,item)=>([...res,{'id':item}]),[]) console.log(c)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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