获取数组中的所有唯一值(删除重复项)用于嵌套的数组/对象。

9
我知道有很多关于唯一数组的答案,但它们无法处理数组中的数组。
我想要的是:
源数组
[
    1,
    0,
    true,
    undefined,
    null,
    false,
    ['a', 'b', 'c'],
    ['a', 'b', 'c'],
    ['a', 'c', 'b'],
    { a: { b: 2 } },
    { a: { b: 2 } },
    { a: { b: 3 } },
    { a: { b: undefined } },
    { a: {  } },
    { a: { b: 3, c: undefined } },
]

归还

[
    1,
    0,
    true,
    undefined,
    null,
    false,
    ['a', 'b', 'c'],
    ['a', 'c', 'b'],
    { a: { b: 2 } },
    { a: { b: 3 } },
    { a: { b: undefined } },
    { a: {  } },
    { a: { b: 3, c: undefined } },
]
  • arr-unique 可以处理 object[],但不能处理数组的数组
  • Set 也不能

失败代码

console.log(array_unique(data));

console.log([...new Set(data)]);

console.log(data.filter(function (el, index, arr)
{
    return index == arr.indexOf(el);
}));

===================

更新

我为此创建了一个模块array-hyper-unique,但没有使用JSON stringify,因为当值为正则表达式时它存在缺陷。


['a', 'b', 'c']['a', 'c', 'b'] 是相同的吗? - Eddie
1
@Eddie 不一样 - bluelovers
4个回答

7

一个简单的方法是将数组和对象进行字符串化以识别重复项:

const input = [
    1,
    true,
    ['a', 'b', 'c'],
    ['a', 'b', 'c'],
    { a: { b: 2 } },
    { a: { b: 2 } },
    { a: { b: 3 } },
    { a: { b: 3, c: undefined } },
];

const outputSet = new Set();
const stringifiedObjs = new Set();
input.forEach(item => {
  if (typeof item !== 'object') outputSet.add(item);
  else {
    // replace undefineds with something, else they'll be ignored by JSON.stringify:
    const stringified = JSON.stringify(
      item,
      (k, v) => v === undefined ? 'undefined-value' : v
    );
    if (!stringifiedObjs.has(stringified)) {
      outputSet.add(item);
      stringifiedObjs.add(stringified)
    }
  }
});
console.log([...outputSet]);


它对我来说可以处理undefined重复项和{ a: { b: undefined } },重复项,你想的是什么? - CertainPerformance
尝试运行 console.log(JSON.stringify({ a: { b: undefined } })); // {"a":{}},可以看到 b 属性已经丢失了。 - Nina Scholz
现在看起来这是最佳答案。 - bluelovers

3
尝试通过使用JSON.stringify将元素转换为字符串,并使用indexOf将这些元素推送到另一个数组中,只有在另一个数组不包含此元素时才推送。然后再次使用map&JSON.parse将字符串转换回原始格式。

var data = [
  1,
  true, ['a', 'b', 'c'],
  ['a', 'b', 'c'],
  {
    a: {
      b: 2
    }
  },
  {
    a: {
      b: 2
    }
  },
  {
    a: {
      b: 3
    }
  },
]
// Create a new array of only string 
// map will give new array and JSON.stringify will convert elements to string
var newData = data.map(function(item) {
  return JSON.stringify(item)
})
//An empty array which will contain only unique values
var uniques = [];
// loop over the array of stirngs and check
//if that value is present in uniques array
//if not then push the element
newData.forEach(function(item) {
  if (uniques.indexOf(item) === -1) {
    uniques.push(item)
  }
});
//Convert array of string to json
var parsedArr = uniques.map(function(item) {
  return JSON.parse(item)
});
console.log(parsedArr)


@NinaScholz他修改了原始数组,随后添加了未定义的属性。 - brk

2
你可以采用递归的方法来处理对象并检查其值。

function check(a, b) {
    if (!a || typeof a !== 'object') {
        return a === b;
    }

    var keys = Object.keys(a);
    return keys.length === Object.keys(b).length
        && keys.every(k => k in b && check(a[k], b[k]));
}

var array = [1, 0, true, undefined, null, false, ['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'c', 'b'], { a: { b: 2 } }, { a: { b: 2 } }, { a: { b: 3 } }, { a: { b: undefined } }, { a: {} }, { a: { b: 3, c: undefined } }],
    unique = array.reduce((r, b) => (r.some(a => check(a, b)) || r.push(b), r), []);
  
console.log(unique);
.as-console-wrapper { max-height: 100% !important; top: 0; }


2
你的方法无法奏效,因为第一个 ['a', 'b', 'c'] 和第二个 ['a', 'b', 'c']不同的对象,以及第一个和第二个实例 { a: { b: 2 } } 也是不同的。因此,即使你将它们添加到 Set 中,它们仍然被视为不相等,因此不能过滤出唯一值。
看起来你想要根据每个对象中的绝对值获取一个唯一的数组。一种简单的方法是使用 ES6 的 Map,例如:
function uniq(arr) {
  var uniqMap = new Map()
  arr.forEach(element => {
    uniqMap.set(JSON.stringify(element), element)
  })
  return [...uniqMap.values()]
} 

您可以获得您想要的结果:
uniq(data)
//Result: [ 1, true, [ 'a', 'b', 'c' ], { a: { b: 2 } }, { a: { b: 3 } } ]

看起来你的答案最接近,如果没有未知的 bug 的话。 - bluelovers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接