【问题标题】:How to prevent lodash mapKeys from reordering my array?如何防止 lodash mapKeys 重新排序我的数组?
【发布时间】:2016-11-02 19:18:39
【问题描述】:

我正在使用 lodash mapKeys 获取我的对象数组并使用 id 属性将其转换为映射对象。这很简单,但问题是它按id 对新对象进行排序。

例如,如果我的数组中有三个对象:

let myArray = [
    {
        id: 3,
        name: 'Number Three'
    },
    {
        id: 1,
        name: 'Number One'
    },
    {
        id: 2,
        name: 'Number Two'
    }
];

然后我通过id映射键:

_.mapKeys(myArray, 'id')

它返回以下内容:

{
    1: {
        id: 1,
        name: 'Number One'
    },
    2: {
        id: 2,
        name: 'Number Two'
    },
    3: {
        id: 3,
        name: 'Number Three'
    }
}

我的服务器以特定顺序返回数组,所以我希望对象保持不变,这样当我遍历对象属性时,它们的顺序正确。

用这种方法可以吗?如果没有,是否有可能的替代方案来实现结果?

【问题讨论】:

  • 这实际上是原生 javascript 行为。 var o = {}; o[3] = 3; o[1] = 1; 产生 o:{1: 1, 3: 3} 为什么这会给您带来问题?如果您要遍历整个列表,则不需要地图格式,如果您通过键 ID 访问对象,则顺序无关紧要。
  • 啊,所以如果没有排序就无法排列对象的属性?
  • @TomNolan JS 对象不是有序数据结构。
  • @WilliamB 这不是“JSON 表示法”,而是“对象文字”。
  • 如果地图和数组引用相同的对象,则不会。

标签: javascript underscore.js lodash


【解决方案1】:

使用Map,因为每个项目都有一个自定义键(如对象),但插入顺序将是迭代顺序(如数组):

const myArray = [
    {
        id: 3,
        name: 'Number Three'
    },
    {
        id: 1,
        name: 'Number One'
    },
    {
        id: 2,
        name: 'Number Two'
    }
];

const map = myArray.reduce((map, item) => map.set(item.id, item), new Map());

map.forEach((item) => console.log(item));

【讨论】:

  • Browser support 可能会有所不同。甚至 IE11 也没有实现Map 的所有功能。
【解决方案2】:

正如 cmets 中所指出的,循环对象并不能保证顺序。如果你想要一个有序列表,你需要一个数组。

但是,您可以应用迭代器模式。在此模式中,由您决定“下一个”元素是什么。因此,您可以拥有一个包含对象的集合(以便在恒定时间内获取它们)和一个数组来存储订单。要进行迭代,您将使用迭代器。

此代码可以作为示例。

希望对你有帮助。

let myArray = [{
  id: 3,
  name: 'Number Three'
}, {
  id: 1,
  name: 'Number One'
}, {
  id: 2,
  name: 'Number Two'
}];

let myIterator = ((arr) => {
  let mySet = _.mapKeys(arr, 'id'),
    index = 0,
    myOrder = _.map(arr, _.property('id'));

  return {
    getObjById: (id) => mySet[id],
    next: () => mySet[myOrder[index++]],
    hasNext: () => index < myOrder.length
  };
})(myArray);

// Access elements by id in constant time.
console.log(myIterator.getObjById(1));

// Preserve the order that you got from your server.
while (myIterator.hasNext()) {
  console.log(myIterator.next());
}
&lt;script src="https://cdn.jsdelivr.net/lodash/4.16.6/lodash.min.js"&gt;&lt;/script&gt;

【讨论】:

    【解决方案3】:

    就像在 cmets 中提到的那样,最好将对象引用保存在数组中以保持顺序,并保存在哈希中以方便更新。

    Backbone's collection (source) 就是这样工作的。它将对象保存在数组 (models) 中,但在添加和删除模型(对象)或模型的 id 更改时自动更新哈希 (_byId)。

    这是该概念的简单实现。您可以自己实现或检查集合库。

    // a little setup
    var array = [];
    var hash = {};
    
    var addObject = function addObject(obj) {
      hash[obj.id] = obj;
      array.push(obj);
    }
    
    // Create/insert the objects once
    addObject({ id: 3, name: 'Number Three' });
    addObject({ id: 1, name: 'Number One' });
    addObject({ id: 2, name: 'Number Two' });
    
    // Easy access by id
    console.log("by id with hash", hash['1']);
    
    // updating is persistent with the object in the array
    hash['1'].name += " test";
    
    // keeps the original ordering
    for (var i = 0; i < 3; ++i) {
      console.log("iterating", i, array[i]);
    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-08
      • 1970-01-01
      • 2019-06-19
      • 1970-01-01
      • 1970-01-01
      • 2020-12-02
      • 2013-03-21
      • 2014-02-08
      相关资源
      最近更新 更多