如果我有以下数组数组:
var myArr = [[0, 1, 2], [1, 2, 6], [9, 10], [10, 11], [11, 12], [13]];
如何合并至少共享一个公共值的数组以产生以下输出?
var myMergedArr = [[0, 1, 2, 6], [9, 10, 11, 12], [13]];
谢谢!
注意:它们并不总是很好地排序,并且在订购所有内容时,共享值可能并不总是开始/结束值。为清楚起见,我订购了上述值。
答案 0 :(得分:3)
可以使用空数组(merged
)作为起始值来减少数组。对于myArray
中的每个数组,existing
被定义为subArray
merged
的{{1}}数组,以便每个subArray
和array
的交集不是空的。
如果找不到这样的数组,existing
将保持未定义,并且新数组(包含在另一个数组中)被定义为existing
并被推送到merged
。
如果找到多个匹配项(existing.slice(1)
不为空),则需要将它们合并在一起:existing[0]
充当所有其他子数组(existing[1..]
)获取的容器合并(没有重复)。然后需要在merged
中找到这些进一步的匹配并将其删除,因为它们已经合并。这可以保证多个数组合并在一起,即使它们之前没有合并,也可以合并。
然后,array
的每个项目都会被推送到existing[0]
(如果尚未包含)。最后,返回merged
。然后,reduce
的下一次迭代可以再次merged
作为第一个参数,并继续myArr
中的下一个数组。
这是ES6的代码。如果需要,您可以将其转换并将其填充到ES5。
var myArr = [
[0, 1, 2],
[1, 2, 6],
[9, 10],
[10, 11],
[11, 12],
[13]
],
myMergedArr = myArr.reduce((merged, array) => {
let existing = merged.filter((subArray) => subArray.filter((subItem) => array.includes(subItem)).length);
if (!existing.length) {
existing = [
[]
];
merged.push(existing[0]);
}
else {
existing.slice(1).forEach((furtherArray) => {
furtherArray.forEach((item) => {
if (!existing[0].includes(item)) {
existing[0].push(item);
}
});
merged.splice(merged.findIndex((subArray) => furtherArray == subArray), 1);
});
}
array.forEach((item) => {
if (!existing[0].includes(item)) {
existing[0].push(item);
}
});
return merged;
}, []);
console.log(myMergedArr);
第二个代码段是相同的代码,但更改了数组。这是为了证明这个脚本可以工作,即使子数组不是有序的:第一个[0, 1, 2]
是独立的,然后[3, 4, 5]
也是独立的 - 两者仍然是分开的。只有稍后[2, 3]
会导致所有先前的数组合并为一个。
var myArr = [
[0, 1, 2],
[3, 4, 5],
[2, 3],
[7, 9],
[9, 10],
[13]
],
myMergedArr = myArr.reduce((merged, array) => {
let existing = merged.filter((subArray) => subArray.filter((subItem) => array.includes(subItem)).length);
if (!existing.length) {
existing = [
[]
];
merged.push(existing[0]);
}
else {
existing.slice(1).forEach((furtherArray) => {
furtherArray.forEach((item) => {
if (!existing[0].includes(item)) {
existing[0].push(item);
}
});
merged.splice(merged.findIndex((subArray) => furtherArray == subArray), 1);
});
}
array.forEach((item) => {
if (!existing[0].includes(item)) {
existing[0].push(item);
}
});
return merged;
}, []);
console.log(myMergedArr);
答案 1 :(得分:1)
disjoint-set data structure似乎非常适合您的情况:
function merge(arrays) {
var ds = new DisjointSet();
arrays.forEach(function(array) {
array.reduce(function(prevSet, currentValue) {
var currentSet = ds.getSet(currentValue);
if(prevSet) prevSet.mergeWith(currentSet);
return currentSet;
}, false);
});
return ds.partitions();
}
var myArr = [[0, 1, 2], [1, 2, 6], [9, 10], [10, 11], [11, 12], [13]];
console.log(JSON.stringify(merge(myArr)));

<script> /* DisjointSet library */
class MySet {
constructor(owner) {
this.rank = 0;
this.parent = this;
this.owner = owner;
}
representative() {
var parent = this.parent;
if(this === parent) return this;
while(parent !== (parent = parent.parent));
this.parent = parent; /* Path compression */
return parent;
}
mergeWith(other) {
var r1 = this.representative(),
r2 = other.representative();
if(r1 === r2) return;
if(r1.owner !== r2.owner) throw new Error("Can't merge");
if(r1.rank > r2.rank) { r2.parent = r1; return; }
r1.parent = r2;
if(r1.rank === r2.rank) ++r1.rank;
}
}
class DisjointSet {
constructor() {
this.sets = new Map();
}
getSet(value) {
var sets = this.sets;
var set = sets.get(value);
if(set) return set;
set = new MySet(this);
sets.set(value, set);
return set;
}
partitions() {
var parts = new Map();
for(var [value,set] of this.sets) {
var repre = set.representative();
var arr = parts.get(repre);
if(arr) arr.push(value);
else parts.set(repre, [value]);
}
return [...parts.values()];
}
}
</script>
&#13;
假设持续的地图操作,摊销时间成本应仅为O(n α(n)) ≈ O(n)
。
每次操作的摊还时间仅为
O(α(n))
,其中α(n)
[...] 对于n
的所有远程实用值,小于5。就这样 每次操作的摊销运行时间实际上是一个很小的常数。
注意我使用ES6映射能够将每个值与其集合相关联。如果所有值都是数字,则不需要这样,然后可以将它们存储为对象属性。但是在partitions
中,您需要提取与集合关联的值,并且存储该数据将需要更多内存。
function merge(arrays) {
var ds = new DisjointSet();
arrays.forEach(function(array) {
array.reduce(function(prevSet, currentValue) {
var currentSet = ds.getSet(currentValue);
if(prevSet) prevSet.mergeWith(currentSet);
return currentSet;
}, false);
});
return ds.partitions();
}
var myArr = [[0, 1, 2], [1, 2, 6], [9, 10], [10, 11], [11, 12], [13]];
console.log(JSON.stringify(merge(myArr)));
&#13;
<script> /* DisjointSet library */
function MySet(value, owner) {
this.rank = 0;
this.parent = this;
this.value = value;
this.owner = owner;
}
MySet.prototype.representative = function() {
var parent = this.parent;
if(this === parent) return this;
while(parent !== (parent = parent.parent));
this.parent = parent; /* Path compression */
return parent;
};
MySet.prototype.mergeWith = function(other) {
var r1 = this.representative(),
r2 = other.representative();
if(r1 === r2) return;
if(r1.owner !== r2.owner) throw new Error("Can't merge");
if(r1.rank > r2.rank) { r2.parent = r1; return; }
r1.parent = r2;
if(r1.rank === r2.rank) ++r1.rank;
};
function DisjointSet() {
this.sets = Object.create(null);
}
DisjointSet.prototype.getSet = function(value) {
var sets = this.sets;
var set = sets[value];
if(set) return set;
set = new MySet(value, this);
sets[value] = set;
return set;
};
DisjointSet.prototype.partitions = function() {
var parts = [];
var assoc = Object.create(null);
var sets = this.sets;
Object.getOwnPropertyNames(sets).forEach(function(value) {
var set = sets[value];
var repreValue = set.representative().value;
var arr = assoc[repreValue];
if(arr) arr.push(set.value);
else parts.push(assoc[repreValue] = [set.value]);
});
return parts;
};
</script>
&#13;
答案 2 :(得分:1)
我相信问题可以用一个相当简单的功能代码来解决,如下所示;
function merger(arr){
return arr.map((e,i,a) => a.slice(i)
.reduce((p,c) => e.some(n => c.includes(n)) ? [...new Set([...p,...c])] : p,[]))
.reduce((r,s) => { var merged = false;
r = r.map(a => a.some(n => s.includes(n)) ? (merged = true, [...new Set([...a,...s])]) : a);
!merged && r.push(s);
return r;
},[]);
}
var arr1 = [[0, 1, 2], [1, 2, 6], [9, 10], [10, 11], [11, 12], [13]],
arr2 = [[0, 1], [2, 3], [1, 2]];
console.log(merger(arr1));
console.log(merger(arr2));
&#13;
关键部分的一点解释。在扩展运算符的帮助下,使用Set对象手动使用Array对象非常容易。所以下面这篇文章可能看起来有点令人困惑,但它确实很重要。
[...new Set([...a,...s])]
假设a
是一个数组或集合,s
是另一个数组或集合。然后[...a,...s]
将两者合并成一个数组。 new Set([...a,...s])
通过删除合并数组中的dupes来创建一个新集。 [...new Set([...a,...s])]
将我们的集合转换为a
和b
连接和删除的数组。酷..!
a.some(n => s.includes(n))
如果数组a
和数组s
至少有一个公共项目返回true
其他false
。