如何从数组中删除包含相同元素的子数组中的所有组合?

5
我有这个数组: [[1,2,3],[4,1,6],[1,3,2],[1,2,4],[3,1,2],[4,6,1],[9,9,9]] 我想要一个函数,实现以下功能: [[1,2,3],[4,1,6],[1,2,4],[9,9,9]] 该函数将删除所有具有相同值的子数组。
我考虑过使用如下过滤器: .filter(el => el.filter(value => array2.includes(value))) 但我认为它不会起作用。
附注:我使用node.js
编辑:奇怪的是,你们很多人的答案在这个例子中完美地工作,但在我的真实程序中却不起作用……事实上,我使用对象而不是数字。像这样:[[obj,obj,obj],[obj,obj,obj],[obj,obj,obj]] 但我不知道为什么它适用于数字,却不适用于对象……
编辑2:现在我提供最小的示例,这个数组可以有20000个项目。
  [
      [
        { name: 'Dofawa', item_type: 'Dofus', level: 6 },
        {
          name: 'Dofus Cawotte',
          item_type: 'Dofus',
          level: 6
        },
        {
          name: 'Dofus Kaliptus',
          item_type: 'Dofus',
          level: 6
        }
      ],
      [
        { name: 'Dofawa', item_type: 'Dofus', level: 6 },
        {
          name: 'Dofus Emeraude',
          item_type: 'Dofus',
          level: 6
        },
        {
          name: 'Dofus Ocre',
          item_type: 'Dofus',
          level: 6
        }
      ],
      [
        { name: 'Dofawa', item_type: 'Dofus', level: 6 },
        {
          name: 'Dofus Kaliptus',
          item_type: 'Dofus',
          level: 6
        },
        {
          name: 'Dofus Cawotte',
          item_type: 'Dofus',
          level: 6
        }
      ],
      [
        {
          name: 'Dofus Cawotte',
          item_type: 'Dofus',
          level: 6
        },
        {
          name: 'Dofus Kaliptus',
          item_type: 'Dofus',
          level: 6
        },
        { name: 'Dofawa', item_type: 'Dofus', level: 6 }
      ]
  ]

应该得到这个对象:

  [
      [
        { name: 'Dofawa', item_type: 'Dofus', level: 6 },
        {
          name: 'Dofus Cawotte',
          item_type: 'Dofus',
          level: 6
        },
        {
          name: 'Dofus Kaliptus',
          item_type: 'Dofus',
          level: 6
        }
      ],
      [
        { name: 'Dofawa', item_type: 'Dofus', level: 6 },
        {
          name: 'Dofus Emeraude',
          item_type: 'Dofus',
          level: 6
        },
        {
          name: 'Dofus Ocre',
          item_type: 'Dofus',
          level: 6
        }
      ]
  ]

1
你的对象无法正常工作,因为它们的toStrings都将是[object][object]而不是"1,2,3"。这显示了提供[mcve]的重要性。您可以尝试使用JSON.stringify而不是sort。 - mplungjan
@mplungjan 不是很清楚,我已经在我的帖子中发布了编辑内容。 - bosskay972
忽略它,“dofus ocre”的效果始终是相同的,我将删除此属性以便更加清晰易懂。 - bosskay972
2
请阅读另一条评论中的内容:“可以有20000个项目”。[询问] - mplungjan
4个回答

2

从技术上讲,可以通过创建哈希表来解决问题,将唯一的数组作为键添加到空的{}对象中。可以通过先对子数组进行排序并将其作为对象的键来创建唯一值。最后,您可以使用Object.values()从对象中获取值。

基于这个想法,我猜这对你有用:

const array = [[1,2,3],[4,1,6],[1,3,2],[1,2,4],[3,1,2],[4,6,1],[9,9,9]];
const result = {};
array.forEach(a => {
  const t = Array.from(a).sort((a,b) => a - b);
  if (!result[t]) result[t] = a;
});

console.log(Object.values(result));

来自 Object.values() 文档:

Object.values() 方法返回一个给定对象的可枚举属性值数组,顺序与 for...in 循环提供的顺序相同(不同之处在于 for-in 循环也会枚举原型链中的属性)。

希望这可以帮到您!


请注意,您的结果与 OP 预期的结果不同。 - mickl
1
@mickl 对的,sort 就是这样做的!我刚刚纠正了一下,我用 Array.from() 克隆了子数组,这就解决了问题。谢谢,干得好! - norbitrial
与数字完美配合,但在我的示例中不起作用,我忘记添加它。现在已经添加到主帖中了。 - bosskay972

2

通过拥有包含对象的数组,并且这些对象具有相同顺序的键,您可以将每个对象作为单个JSON字符串获取并对JSON字符串数组进行排序,然后将这些字符串连接成一个字符串,并将此字符串作为值用于使用Set检查唯一性。

const
    normalize = v => v.map(o => JSON.stringify(o)).sort().join('|'),
    filterWithSet = s => v => (n => !s.has(n) && s.add(n))(normalize(v)),
    data = [[{ name: 'Dofawa', item_type: 'Dofus', level: 6 }, { name: 'Dofus Cawotte', item_type: 'Dofus', level: 6 }, { name: 'Dofus Kaliptus', item_type: 'Dofus', level: 6 }], [{ name: 'Dofawa', item_type: 'Dofus', level: 6 }, { name: 'Dofus Emeraude', item_type: 'Dofus', level: 6 }, { name: 'Dofus Ocre', item_type: 'Dofus', level: 6 }], [{ name: 'Dofawa', item_type: 'Dofus', level: 6 }, { name: 'Dofus Kaliptus', item_type: 'Dofus', level: 6 }, { name: 'Dofus Cawotte', item_type: 'Dofus', level: 6 }], [{ name: 'Dofus Cawotte', item_type: 'Dofus', level: 6 }, { name: 'Dofus Kaliptus', item_type: 'Dofus', level: 6 }, { name: 'Dofawa', item_type: 'Dofus', level: 6 }]],
    unique = data.filter(filterWithSet(new Set));

console.log(unique);
.as-console-wrapper { max-height: 100% !important; top: 0; }


更多类似于这样的:[[{a: "zaza", b:"zizi", c:"zozo"},{a: "zaza1", b:"zizi1", c:"zozo1"},{a: "zaza2", b:"zizi2", c:"zozo2"}],[{a: "zaza1", b:"zizi1", c:"zozo1"},{a: "zaza", b:"zizi", c:"zozo"},{a: "zaza2", b:"zizi2", c:"zozo2"}],[{a: "zaza", b:"zizi", c:"zozo"},{a: "zaza1", b:"zizi1", c:"zozo1"},{a: "sbeve2", b:"sbive2", c:"sbove2"}],[{a: "zaza", b:"zizi", c:"zozo"},{a: "sbeve2", b:"sbive2", c:"sbove2"},{a: "zaza1", b:"zizi1", c:"zozo1"}]] - bosskay972
1
对象的属性是否总是具有相同的顺序? - Nina Scholz
外部数组中的对象顺序是什么? - Nina Scholz
每个子数组都有这种对象组合: [ { name: 'Dofawa', item_type: 'Dofus', level: 6 }, { name: 'Dofus Cawotte', item_type: 'Dofus', level: 6 }, { name: 'Dofus Kaliptus', item_type: 'Dofus', level: 6 } ] 每个对象都具有相同数量的属性,并且在属性方面具有相同的顺序。请参阅主帖中的示例。 - bosskay972

1

你可以使用 Array.reduce 构建一个字典,其键为已排序的数组,然后使用 Object.values 检索非重复数组:

let input = [[1,2,3],[4,1,6],[1,3,2],[1,2,4],[3,1,2],[4,6,1],[9,9,9]]

let dict = input.reduce((state, current) => {
   let key = [...current].sort((a,b) => a - b).toString(); 
   if(!state[key]){
      state[key] = current;
   }
   
   return state;

}, {});

let result = Object.values(dict);

console.log(result);


0
使用reduce()函数并使用JSON.stringify()将当前排序的数组与已添加到累加器中的数组进行比较。

const arr = [[1,2,3],[4,1,6],[1,3,2],[1,2,4],[3,1,2],[4,6,1],[9,9,9]];

const res = arr.reduce((acc,cur) => {
    let match = false;
    acc.forEach(el => {
       if(JSON.stringify(cur.concat().sort()) === JSON.stringify(el.concat().sort())){
          match = true;
       }
    });

    return match ? acc : acc.concat([cur]);
},[]);

console.log(res);


请注意,您的结果与 OP 预期的结果不同。 - mickl
el.sort() 正在改变 arr 变量中原始的子数组,我也遇到了同样的情况。 - norbitrial
你的答案看起来不错,但是在我的真实程序中它似乎非常沉重,我等了20多秒仍然没有反应,我正在处理一个大约有20000个元素的数组... - bosskay972

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