【问题标题】:underscore/lodash unique by multiple properties多个属性唯一的下划线/lodash
【发布时间】:2014-12-06 00:52:04
【问题描述】:

我有一个包含重复对象的数组,我正在尝试获取一个唯一列表,其中唯一性由对象属性的子集定义。例如,

{a:"1",b:"1",c:"2"}

我想在唯一性比较中忽略c

我可以做类似的事情

_.uniq(myArray,function(element) { return element.a + "_" + element+b});

我希望我能做到

_.uniq(myArray,function(element) { return {a:element.a, b:element.b} });

但这不起作用。有没有我可以做的事情,或者如果我要比较多个属性,我是否需要创建对象的可比较表示?​​

【问题讨论】:

  • 你为什么要尝试第二次?第一个有效,对吧?
  • 是的,第一个是有效的,但是必须进行字符串连接感觉有点笨拙。试图了解是否有更自然的方法来做到这一点。
  • 对象始终是唯一的,因此您需要按单个属性值进行比较,而不是按整个对象进行比较。使用字符串比较可以处理某些数据,但不能处理其他数据,例如:使用如图所示的数字字符串,您有可能将 {a:"1"} 与 {a:1}.s 冲突
  • _.uniq([{a:"1",b:"1",c:"2"},{a:"1",b:"2",c:"2 "},{a:"1",b:"1",c:"2"}], JSON.stringify); JSON 顺序无法保证,但我不明白为什么这在单个浏览器中不起作用。
  • 在我的特殊情况下,我只是比较字符串。 @dandavis 我不想比较所有属性,只比较其中的一个子集

标签: javascript underscore.js unique lodash


【解决方案1】:

不幸的是,似乎没有一种直接的方法可以做到这一点。如果没有为此编写自己的函数,您需要返回可以直接比较相等性的内容(如您的第一个示例中所示)。

一种方法是 .join() 您需要的属性:

_.uniqBy(myArray, function(elem) { return [elem.a, elem.b].join(); });

或者,您可以使用_.pick_.omit 删除不需要的任何内容。从那里,您可以使用_.values.join(),甚至可以只使用JSON.stringify

_.uniqBy(myArray, function(elem) {
    return JSON.stringify(_.pick(elem, ['a', 'b']));
});

请记住,就属性顺序而言,对象不是确定性的,因此您可能只想坚持使用显式数组方法。

附:将 Lodash

uniqBy 替换为 uniq

【讨论】:

  • 使用join('')是漏洞百出(如[1,23][12,3])。
  • @muistooshort:好点 - 我想标准的逗号分隔连接会更好。
  • @voithos:但是如果数据包含逗号怎么办?或者如果一个数字被/没有被引用?
  • @dandavis:我想你可以在阵列上做JSON.stringify,而不是使用join。但实际上,重写uniq 以考虑多个属性会更容易。
  • lodash 4 自带_.uniqWith(myArray, _.isEqual)
【解决方案2】:

使用 Lodash 的 uniqWith 方法:

_.uniqWith(array, [comparator])

这个方法类似于_.uniq,只是它接受comparator,它被调用来比较array的元素。结果值的顺序由它们在数组中出现的顺序决定。比较器使用两个参数调用:(arrVal, othVal)

comparator 返回true 时,这些项目被认为是重复的,并且只有第一个出现的项目会包含在新数组中。


示例:
我有一个带有latitudelongitude 坐标的位置列表——其中一些是相同的——我想查看具有唯一坐标的位置列表:

const locations = [
  {
    name: "Office 1",
    latitude: -30,
    longitude: -30
  },
  {
    name: "Office 2",
    latitude: -30,
    longitude: 10
  },
  {
    name: "Office 3",
    latitude: -30,
    longitude: 10
  }
];

const uniqueLocations = _.uniqWith(
  locations,
  (locationA, locationB) =>
    locationA.latitude === locationB.latitude &&
    locationA.longitude === locationB.longitude
);

// Result has Office 1 and Office 2

【讨论】:

  • 这应该是答案
  • 这可行,但需要在函数内返回
  • @Franco 箭头函数使用隐式returnRead more here
【解决方案3】:

这里有正确答案

javascript - lodash - create a unique list based on multiple attributes

仅供参考var result = _.uniqBy(list, v => [v.id, v.sequence].join());

【讨论】:

    【解决方案4】:

    我确实认为 join() 方法仍然是最简单的。尽管在之前的解决方案中提出了担忧,但我认为选择正确的分隔符是避免已识别陷阱的关键(不同的值集返回相同的连接值)。请记住,分隔符不必是单个字符,它可以是您确信不会在数据本身中自然出现的任何字符串。我一直这样做并且喜欢使用 '~!$~' 作为我的分隔符。它还可以包含特殊字符,如 \t\r\n 等。

    如果包含的数据确实如此不可预测,那么最大长度可能是已知的,您可以在加入之前简单地将每个元素填充到其最大长度。

    【讨论】:

      【解决方案5】:

      @voithos 和@Danail 的组合答案中有一个提示。我解决这个问题的方法是在我的数组中的对象上添加一个唯一键。

      起始样本数据

      const animalArray = [
        { a: 4, b: 'cat', d: 'generic' },
        { a: 5, b: 'cat', d: 'generic' },
        { a: 4, b: 'dog', d: 'generic' },
        { a: 4, b: 'cat', d: 'generic' },
      ];
      

      在上面的示例中,我希望数组对于 ab 是唯一的,但现在我有两个具有 a: 4b: 'cat' 的对象。通过将 a + b 组合成一个字符串,我可以获得一个唯一的键来检查。

      { a: 4, b: 'cat', d: 'generic', id: `${a}-${b}` }. // id is now '4-cat'
      

      注意:您显然需要映射数据或在创建对象期间执行此操作,因为您无法在同一对象中引用对象的属性。

      现在比较很简单……

      _.uniqBy(animalArray, 'id');
      

      生成的数组长度为 3,它将删除最后一个重复项。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-02-20
        • 2016-09-15
        • 1970-01-01
        • 2018-05-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多