【问题标题】:Sort an array of objects based on another array of ids根据另一个 id 数组对对象数组进行排序
【发布时间】:2016-06-02 23:40:04
【问题描述】:

我有 2 个数组

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

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

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

我更喜欢使用 Ramda 或常规 JS。

【问题讨论】:

标签: javascript ramda.js


【解决方案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,使用ArrayES2015标准)的一些方法

              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)
                &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"&gt;&lt;/script&gt;

                【讨论】:

                  【解决方案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),
                  );
                  &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.js" integrity="sha512-3sdB9mAxNh2MIo6YkY05uY1qjkywAlDfCf5u1cSotv6k9CZUSyHVf4BJSpTYgla+YHLaHG8LUpqV7MHctlYzlw==" crossorigin="anonymous"&gt;&lt;/script&gt;

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2021-11-20
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-04-05
                    • 1970-01-01
                    相关资源
                    最近更新 更多