查找是否有两个数组在一个数组中重复,并选择它们。

8

我有一个主/父数组,其中包含多个子数组,就像这样:

var array = [[1, 17], [1, 17], [1, 17], [2, 12], [5, 9], [2, 12], [6, 2], [2, 12]];

以下是数组,以便更简单地阅读:
[1, 17]
[1, 17]
[1, 17]
[2, 12]
[5, 9]
[2, 12]
[6, 2]
[2, 12]
[2, 12]

我想选择重复3次或更多次(> 3)的数组,并将其分配到一个变量中。所以在这个例子中,var repeatedArrays将是[1, 17][2, 12]

所以最终结果应该是:

console.log(repeatedArrays);
>>> [[1, 17], [2, 12]]

我在这里找到了类似的内容(链接),但它使用了underscore.js和lodash。

如果需要,我该如何使用javascript甚至是jquery来实现它?


你可以将它们与它们的JSON值进行比较,但是[1, 17]不会匹配[17, 1] - Get Off My Lawn
你可以通过点击左侧的大灰色勾选按钮来接受一个答案(如果它对你有帮助)。如果你愿意,你可以通过点击上方的灰色三角形为任何好的答案的作者添加+10分。 - Kamil Kiełczewski
6个回答

15

试试这个

array.filter(( r={}, a=>!(2-(r[a]=++r[a]|0)) ))

var array = [[1, 17], [1, 17], [1, 17], [2, 12], [5, 9], [2, 12], [6, 2], [2, 12]];

var r= array.filter(( r={}, a=>!(2-(r[a]=++r[a]|0)) ))

console.log(JSON.stringify(r));

时间复杂度 O(n)(通过过滤函数对一个数组进行一次遍历)。受Nitish答案启发。

解释

< p > (r = {},a => ...)将返回逗号后的最后一个表达式(即a => ...)(例如(5,6)== 6 )。在r = {} 中,我们设置了一次临时对象,其中我们将存储唯一键。在过滤函数a => ...中,在a中,我们有当前的array元素。在r [a]中,JS会将a隐式转换为字符串(例如1,17)。然后在!(2-(r [a] = ++ r [a] | 0))中,我们增加了元素a的出现次数计数器,并返回true(作为过滤函数值),如果元素a出现3次。如果r [a]未定义,则 ++ r [a] 返回 NaN ,进一步 NaN | 0 = 0 (也 number | 0 = number )。 r [a] = 初始化第一个计数器值,如果省略它,则 ++ 仅将 NaN 设置为 r [a] ,而 NaN 是不可增量的(因此我们需要在init中放置零)。如果我们删除 2- ,则结果是我们得到没有重复项的输入数组 - 或者我们也可以通过 a =>!(r [a] = a in r)来获取这个结果。如果我们将 2- 更改为 1- ,我们将得到仅包含重复项的数组.

更新

基于@kencomment,可以编写更短的版本(它应该始终适用于数字数组)。@ken代码的原始较长版本在片段中显示,展示了@ken如何巧妙地使用.filter()的第二个参数来避免使用全局变量r

array.filter(a=>!(2-(this[a]=++this[a]|0)))

var array = [[1, 17], [1, 17], [1, 17], [2, 12], [5, 9], [2, 12], [6, 2], [2, 12]];

var r= array.filter(a=>!(2-(this[a]=++this[a]|0)), {})

console.log(JSON.stringify(r));


2
有些人就是喜欢挑战极限。敬意。B) - Nelson Teixeira
1
我刚刚花了大约半个小时来理解你所做的事情。现在我理解了,更加欣赏它。如果可以的话,我会为此奖励你。如果其他人也感到震惊,我已经将其分成几个部分,并提供了一个帮助性的示例,以便在这里进行学习:https://jsfiddle.net/drpfe3hm/3/。 - Nelson Teixeira
1
在不使用逗号返回并滥用 Array#filter 的第二个参数(thisArg)的情况下:array.filter(a=>!(2-(this [a] = ++ this [a] | 0)),{}) - ken
1
@ken - 感谢您的改进 - 很好 - 我认为这甚至可以更短地写成 array.filter(a=>!(2-(this[a]=++this[a]|0))) - Kamil Kiełczewski
1
@ken,我很难过。我宁愿不知道Vyxal和Jelly这样的东西存在。 :D - Nelson Teixeira
显示剩余3条评论

10
你可以使用已经转成字符串的数组创建一个 Map ,然后进行计数、过滤和恢复数组。

var array = [[1, 17], [1, 17], [1, 17], [2, 12], [5, 9], [2, 12], [6, 2], [2, 12]],
    result = Array
        .from(array.reduce(
            (map, array) =>
                (json => map.set(json, (map.get(json) || 0) + 1))
                (JSON.stringify(array)),
            new Map
         ))
        .filter(([, count]) => count > 2)
        .map(([json]) => JSON.parse(json));
        
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

使用map函数过滤至所需数量。

var array = [[1, 17], [1, 17], [1, 17], [2, 12], [5, 9], [2, 12], [6, 2], [2, 12]],
    result = array.filter(
        (map => a => 
            (json =>
                (count => map.set(json, count) && !(2 - count))
                (1 + map.get(json) || 1)
            )
            (JSON.stringify(a))
        )
        (new Map)
    );
        
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

独特!

var array = [[1, 17], [1, 17], [1, 17], [2, 12], [5, 9], [2, 12], [6, 2], [2, 12]],
    result = array.filter(
        (s => a => (j => !s.has(j) && s.add(j))(JSON.stringify(a)))
        (new Set)
    );
        
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }


为此任务,您可以使用 Set 而不是 Map - Nina Scholz
请问您能否编辑您的答案并展示给我看吗?我需要使用has()函数吗? - Timmy Balk
@TimmyBalk 只需删除 .filter(([, count]) => count > 2) 这一行代码 - 不要改变其他任何内容。 - Kamil Kiełczewski
@KamilKiełczewski 太棒了! 非常感谢。我现在要研究 filter() 函数,再次感谢 Nina 和 Kamil。但我认为删除 .filter 行并不等同于 Nina 所说的是吗? - Timmy Balk
@NinaScholz,关于您更新的代码,如果只有3个或更多个重复项,我该如何设置仅保留一个?或者例如,5个或更多个重复项呢?我该如何设置? - Timmy Balk
显示剩余6条评论

5
您可以使用以下方式之一来实现:Object.reduceObject.entries

var array = [[1, 17], [1, 17], [1, 17], [2, 12], [5, 9], [2, 12], [6, 2], [2, 12]];


let res = Object.entries(
            array.reduce((o, d) => {
              let key = d.join('-')
              o[key] = (o[key] || 0) + 1

              return o
          }, {}))
          .flatMap(([k, v]) => v > 2 ? [k.split('-').map(Number)] : [])
  
  
console.log(res)

或者仅使用 Array.filters

来实现。

var array = [[1, 17], [1, 17], [1, 17], [1, 17], [2, 12], [5, 9], [2, 12], [6, 2], [2, 12]];

let temp = {}
let res = array.filter(d => {
  let key = d.join('-')
  temp[key] = (temp[key] || 0) + 1
  
  return temp[key] == 3
})

console.log(res)


1
第一段不符合要求(返回的元素是字符串而不是数字)——但第二段非常棒,+1。 - Kamil Kiełczewski
谢谢@KamilKiełczewski。更新了第一个片段以返回数字。 - Nitish Narang

4

如果你想换一种方式,可以先对列表进行排序,然后循环一次并提取满足要求的元素。这可能比从数组中将键字符串化更快,即使进行了排序:

var arr = [[1, 17], [1, 17], [1, 17], [2, 12], [5, 9], [2, 12], [6, 2], [2, 12]]
arr.sort((a, b) => a[0] - b[0] || a[1] - b[1])

// define equal for array
const equal = (arr1, arr2) => arr1.every((n, j) => n === arr2[j])

let GROUP_SIZE = 3
first = 0, last = 1, res = []

while(last < arr.length){
    if (equal(arr[first], arr[last])) last++
    else {
        if (last - first >= GROUP_SIZE)  res.push(arr[first])
        first = last
    }
}
if (last - first >= GROUP_SIZE)  res.push(arr[first])
console.log(res)


有趣的方法,但它真的更快吗? - Timmy Balk
1
@TimmyBalk 这不是对 JSON 方法的批评 - 我认为它很好,但如果我的测试正确的话,在我的浏览器上它速度较慢。这是一个 jsperf 测试:https://jsperf.com/2json-v-sort-loop - Mark

2
您也可以使用单个Array.reduce来完成此操作,只有在长度等于3时才将其推送到result属性中:

var array = [[1, 17], [1, 17], [1, 17], [1, 17], [2, 12], [5, 9], [2, 12], [6, 2], [2, 12]];

console.log(array.reduce((r,c) => {
  let key = c.join('-')
  r[key] = (r[key] || 0) + 1
  r[key] == 3 ? r.result.push(c) : 0  // if we have a hit push to result
  return r
}, { result: []}).result)             // print the result property


1

ES6:

const repeatMap = {}

array.forEach(arr => {
  const key = JSON.stringify(arr)
  if (repeatMap[key]) {
    repeatMap[key]++
  } else {
    repeatMap[key] = 1
  }
})

const repeatedArrays = Object.keys(repeatMap)
  .filter(key => repeatMap[key] >= 3)
  .map(key => JSON.parse(key))

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