使用另一个数组的元素变换数组,同时维护顺序和现有实例

时间:2018-02-18 21:36:12

标签: javascript arrays masking mutation

需要有关两个数组的算法的帮助,并在它们之间进行特定的比较,以保持相同的顺序:

  • 如果现有_ID但_ID不在覆盖中,则不在结果中包含元素
  • 如果_ID在覆盖中,但_ID不在现有中,则将元素添加到RESULT
  • 如果_ID同时存在于现有和覆盖中,则将现有的_ID版本/实例的元素添加到结果中

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,而不是遍历所有现有的数组。

主要是我正在寻找尽可能快的东西

我非常感谢你能分享的帮助。

3 个答案:

答案 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;
&#13;
&#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)时间复杂度。然后,覆盖数组上的循环可以在每次获取的恒定时间内获取相应的元素。

以下是一个例子:

&#13;
&#13;
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;
&#13;
&#13;