需要有关两个数组的算法的帮助,并在它们之间进行特定的比较,以保持相同的顺序:
captial letter表示引用_ID v#表示引用_ID
的版本/实例1
现有:[Av1 Bv1 Cv1 Ev1 Fv1 Gv1]
覆盖:[Bv2 Dv1 Fv2 Hv1 Jv1]
结果:[Bv1 Dv1 Fv1 Hv1 Jv1]
2
现有:[Bv1 Cv1 Ev1 Fv1 Gv1]
覆盖:[Av1 Bv2 Dv1 Fv2 Hv1 Jv1]
结果:[Av1 Bv1 Dv1 Fv1 Hv1 Jv1]
3
现有:[Av1 Bv1 Cv1 Ev1 Fv1 Gv1]
覆盖:[Av2 Bv2 Dv1 Fv2 Hv1 Jv1]
结果:[Av1 Bv1 Dv1 Fv1 Hv1 Jv1]
4
现有:[Cv1 Dv1 Ev1]
覆盖:[Av1 Bv1 Dv2 Fv1 Hv1 Jv1]
结果:[Av1 Bv1 Dv1 Fv1 Hv1 Jv1]
5
现有:[Av1 Bv1 Cv1 Ev1 Fv1 Gv1]
覆盖:[Dv1]
结果:[Dv1]
我正在寻找一个log(n)函数,它在一次传递中执行这个变异操作(可能有现有和覆盖的枢轴索引,但我不确定)。
不想使用indexOf。
这是我使用缩小尾部技术的log(n ^ 2)解决方案:
let eIndx = 0,
existing = [ Av1, Bv1, Cv1, Ev1, Fv1, Gv1 ],
overwriter = [ Bv2, Dv1, Fv2, Hv1, Jv1 ];
overwriter.map( element => {
while( element._ID !== existing[ eIndx ]._ID &&
eIndx !== existing.length ) {
delete existing[ eIndx ];
++eIndx;
}
return eIndx !== existing.length ? existing[ eIndx ] : element;
});
如果现有的_ID都没有覆盖,则此解决方案速度最慢。
我不确定是否应该遍历现有数组或覆盖数组。
在我发布的扩展(更复杂)解决方案中,我通过覆盖器进行了迭代,我有一个哈希字典来引用未来是否存在_ID /版本组合(以后的版本)还没有迭代)数组。我不能再使用那个全局字典了,我试图弄清楚是否需要为每个数组实例制作一个本地字典,或者是否有一种方法我不需要字典并只使用枢轴进行比较。我在log(n)解决方案中看到的一个问题是,它不知道覆盖器的第一个元素是否是一个新的_ID,而不是遍历所有现有的数组。
主要是我正在寻找尽可能快的东西
我非常感谢你能分享的帮助。
答案 0 :(得分:3)
您可以使用Map
并检查是否存在ID。
function merge(existing, overwriter) {
const getId = s => s.split('v')[0]; // or whatever is suitable
var versions = new Map;
existing.forEach(s => versions.set(getId(s), s));
return overwriter.map(s => versions.get(getId(s)) || s);
}
console.log(merge([ 'Av1', 'Bv1','Cv1','Ev1','Fv1', 'Gv1' ],[ 'Bv2', 'Dv1','Fv2','Hv1','Jv1']));

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

答案 1 :(得分:1)
我发布此答案作为讨论的基础,即使based on previous discussion,我知道它并不能满足您的需求。
它是递归的,在Javascript中,可能仍然意味着它很慢或者可能溢出堆栈。尽管如此,它已准备好进行尾部调用优化,因此最终它不应该太慢。但是递归,代码非常干净。 但是,它基于我从您的示例中做出的假设,即列表已经过排序。
const original = [{"_ID": 2, "val": "a0"}, {"_ID": 3, "val": "a1"}, {"_ID": 5, "val": "a2"}, {"_ID": 7, "val": "a3"}, {"_ID": 11, "val": "a4"}, {"_ID": 13, "val": "a5"}, {"_ID": 17, "val": "a6"}, {"_ID": 19, "val": "a7"}]
const overwriter = [{"_ID": 1, "val": "b0"}, {"_ID": 2, "val": "b1"}, {"_ID": 3, "val": "b2"}, {"_ID": 5, "val": "b3"}, {"_ID": 8, "val": "b4"}, {"_ID": 13, "val": "b5"}, {"_ID": 21, "val": "b6"}, {"_ID": 34, "val": "b7"}]
const mergeLists = (a, b, combined = []) => a.length == 0
? combined.concat(b)
: b.length == 0
? combined
: a[0]._ID < b[0]._ID
? mergeLists(a.slice(1), b, combined)
: a[0]._ID == b[0]._ID
? mergeLists(a.slice(1), b.slice(1), combined.concat([a[0]]))
: mergeLists(a, b.slice(1), combined.concat([b[0]]))
console.log(mergeLists(original, overwriter))
&#13;
现在我们已经发现它们没有排序,我认为没有比在本地重新创建索引更好的了。它肯定比每次搜索列表更快(O(m * n)
。)
使用索引对象的属性查找只比O(1)
慢一点,因此,以额外空间为代价,您仍然可以在O(m + n)
中执行此操作。
该代码应该非常简单:通过将现有数据减少为对象来创建索引,键入_ID
属性,然后迭代覆盖器,在输出中放入索引中匹配的数据,如果它存在,否则被覆盖。
这仍然没有变异。您可以使用适当的splice
来实现这一目标,但如果您这样做,请确保反向迭代。但我建议不要在任何情况下改变。
在任何情况下,此代码都适用于我上面使用的数据。如果您的ID不是字符串或数字,则应使用Nina的Map
版本代替:
const mergeLists = (original, overwriter) => {
const index = original.reduce((idx, val) => (idx[val._ID] = val, idx), {})
return overwriter.map(val => index[val._ID] || val)
}
答案 2 :(得分:0)
您说的是 log(n)和 log(n 2 ),但显然意味着 O(n)和 O(n 2 )。
如果您首先为现有值创建地图,并按其ID值键入,则可以实现 O(n)时间复杂度。然后,覆盖数组上的循环可以在每次获取的恒定时间内获取相应的元素。
以下是一个例子:
const existing = [
{ _id: "A", version: 1 },
{ _id: "B", version: 1 },
{ _id: "C", version: 1 },
{ _id: "E", version: 1 },
{ _id: "F", version: 1 },
{ _id: "G", version: 1 },
],
overwriter = [
{ _id: "B", version: 2 },
{ _id: "D", version: 1 },
{ _id: "F", version: 2 },
{ _id: "H", version: 1 },
{ _id: "J", version: 1 },
];
const map = new Map(existing.map( o => [o._id, o] ));
const result = overwriter.map( el => map.get(el._id) || el );
console.log(result);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;