在多维javascript数组中查找唯一条目,命令重要依赖于级别

时间:2017-01-15 22:27:39

标签: javascript jquery arrays multidimensional-array unique

在多维javascript数组中查找所有唯一的第一级条目最优雅的解决方案是什么?只有一条重要规则:条目的顺序仅在第一级重要,但在第二级不重要

例如,对于以下数组,脚本应返回4个唯一条目(第一个,第三个,第四个和第五个):

[
  [ [],[22],[1,13,17],[12],[] ], 
  [ [],[22],[17,13,1],[12],[] ], 
  [ [],[12],[1,13,17],[22],[] ], 
  [ [11],[12],[13],[14],[15] ], 
  [ [15],[14],[13],[12],[11] ]
]

PS。也可以使用jQuery。

3 个答案:

答案 0 :(得分:2)

首先,这是一个有效的JSFiddle供您使用:http://jsfiddle.net/missyalyssi/ro8o94nk/

给定一个输入数组,函数findUnique将返回一个数组,其中包含根据您的定义唯一的项。所以,例如:

[[8],[1,2,3],[9]]是[[8],[3,1,2],[9]]的副本,但不是[[8],[3,1,2],[9]]的副本。 9],[3,1,2],[8]]

我写这篇文章的主要焦点是让它易于阅读和理解。

function findUnique(input) {
  let found = [];
  let uniqueEls = new Set();
  let hasDup = true;
  for (let element of input) {
    hasDup = found.length && 
             found.every((el) => {return deepEqualsNaive(el, element)});
    if (hasDup) {
      uniqueEls.delete(element);
      continue;
    }
    found.push(element);
    uniqueEls.add(element);
  }
  return [...uniqueEls];
}

此函数使用deepEqualsNaive来确定两个数组是否相等。由于javascript中的对象相等意味着数组将指向相同的内存位置,因此我们需要构建自己的函数,以便为我们调用的内容返回true。在这里,相同我们的意思是它们具有相同的元素,即使它们没有指向相同的存储位置,或者以相同的顺序出现。

为了便于阅读,我已经递归地编写了这个函数我不知道你正在使用它的上下文。如果你可以溢出堆栈然后使用迭代版本。

以下是一些示例输入和我们期望的内容:

deepEqualsNaive([ [],[22],[1,13,17],[12],[] ], [ [],[22],[17,13,1],[12],[] ]) => true

deepEqualsNaive([ [],[22],[17,13,1],[12],[] ], [ [],[12],[1,13,17],[22],[] ]) => false

deepEqualsNaive([ [],[22],[1,13,17],[12],[] ], [ [],22,[17,13,1],[12],[] ]) => false

功能:

function deepEqualsNaive (input, clone) {
  if (!Array.isArray(input) || !Array.isArray(clone)) return false;
  if (input.length !== clone.length) return false;
  var result = 0;
  for (let elIdx = 0; elIdx < input.length; elIdx++) {
    var tryDeep = true;
    if (Array.isArray(input[elIdx])) tryDeep = deepEqualsNaive(input[elIdx], clone[elIdx]);
    if (!tryDeep) return false;
    result ^= input[elIdx];
    result ^= clone[elIdx];
  }
  return result === 0;
}

答案 1 :(得分:1)

如果可以引用原始数组'records'和内部数组(即没有深层副本),你可以使用类似的东西:

function distinct(arr){
    const res =[], //array with results
     cmpArr = (a1,a2) => a1.length===a2.length && a1.every((i,ind) => a2[ind] === i),
     cmpRec = (a1,a2) => [1,2,3].every(i=> cmpArr(a1[i],a2[i])); //compare 'records' for indices 1,2 and 3    
  for(let subarr of arr){  	
    subarr[2].sort(); //NB, this alters the source array. If this is not allowed, a work around can be created
    if(!res.some(r => cmpRec(r,subarr))) //check if 'res' doesn't have an entry , based on the cmpRec function
    	res.push(subarr);      
  }
  return res;
}

//test:
let input = [
  [ [],[22],[1,13,17],[12],[] ], 
  [ [],[22],[17,13,1],[12],[] ], 
  [ [],[12],[1,13,17],[22],[] ], 
  [ [11],[12],[13],[14],[15] ], 
  [ [15],[14],[13],[12],[11] ]
];

console.log(distinct(input).map(JSON.stringify)); 

答案 2 :(得分:1)

如果你不是那么担心性能而只是需要一些有效的东西,你可以使用你提到的恒定深度和字符串表示作为各种类型的“指纹”(类似于Java的hashcode

然后您使用Set跟踪之前未曾见过的项目,并仅添加新项目。

function getUnique(rows) {
  let unique = new Set();
  let results = [];
  for (let row of rows) {
    // Fingerprint is the string representation of the row,
    // with the inner-level sorted (as order doesn't matter).
    // E.g., fingerprint of [ [8], [3, 2, 1], [9] ] is '[[8],[1,2,3],[9]]'
    let fingerprint = JSON.stringify(row.map((cells) => {
      return cells.concat().sort();  // Use concat to avoid sorting in place.
    }));

    // If we haven't seen this fingerprint before,
    // add to the filter and the results list.
    if (!unique.has(fingerprint)) {
      unique.add(fingerprint);
      results.push(row);
    }
  }
  return results;
}

例如,这将提出......

> x = [
... [ [8], [3, 2, 1], [9] ],
... [ [7], [8, 3, 9], [1, 2] ],
... [ [8], [1, 2, 3], [9] ],
... ];
> getUnique(x);
[ [ [ 8 ], [ 3, 2, 1 ], [ 9 ] ],
  [ [ 7 ], [ 8, 3, 9 ], [ 1, 2 ] ] ]

显然,如果你的内部值是非基元(对象,数组等),那么这将会失败,但是如果你正在处理像你的例子这样的数字,它应该没问题。