繁体   English   中英

使用JavaScript的“完全外部联接”

[英]“Full outer join” using JavaScript

我不介意使用库来解决此问题,尤其是如果它简化了代码。

我有一些这样的数据:

 [{source: 'b', foo: 'bar'},
   {source:'d', foo: 'baz'}]

我还有另一个像这样的数组:

['b', 'c', 'e']

我想处理这两个数组并将其作为输出:

 [{source: 'b', foo: 'bar'},
    {source: 'c', foo: 'someDefaultValue'}, 
    {source:'d', foo: 'baz'}, 
    {source: 'e', foo: 'someDefaultValue'}]

详细地说,如果数据在第一个数组中,则应保留在结果中。 如果数据在第二个数组中,则它应以默认值出现在结果中。 我希望结果按source排序。

用SQL术语,我将其称为“源列上的完全外部联接”。 我发现很难以这种方式在JavaScript中编写代码。 给定两个输入,如何得到结果?

您可以采用两个对象数组和一个进行联接的键来收集映射中的所有值。

 function fullOuterJoin(left, right, on) { const getV = o => o[on], getMap = (m, o) => m.set(getV(o), Object.assign(m.get(getV(o)) || {}, o)); return Array .from(left.reduce(getMap, right.reduce(getMap, new Map)).values()) .sort((a, b) => getV(a).localeCompare(getV(b))); } var left = [{ source: 'b', foo: 'bar' }, { source:'d', foo: 'baz' }], source = ['b', 'c', 'e'], right = source.map(source => ({ source, foo: 'someDefaultValue' })), result = fullOuterJoin(left, right, 'source'); console.log(result); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

你可以做这样的事情

  • 创建一个以源为键的Map
  • 使用arr1值初始化final ,如果您不需要不变性,则可以直接按下arr1
  • 循环访问arr2并检查映射器中的值是否不可用,以默认值压入final
  • 根据来源排序

 let arr1 = [{source: 'b', foo: 'bar'}, {source:'d', foo: 'baz'}] let arr2 = ['b', 'c', 'e'] let final = [...arr1] let mapper = new Map(arr1.map(v=>[v.source,v.source])) arr2.forEach(val=>{ if(!mapper.has(val)){ final.push({source:val, foo:'default'}) } }) final.sort((a,b)=> a.source.localeCompare(b.source)) console.log(final) 

我假设您想将结果合并到一个新数组中,而不是修改输入。

  • 索引array 1源值
  • 迭代array 2并检查相应的源是否存在。 如果是,请跳过它,否则请添加一个。

(可选)您可以根据需要按source对结果进行排序。

干得好:

 let data1 = [{ source: 'b', foo: 'bar' }, { source: 'd', foo: 'baz' }]; let data2 = ['b', 'c', 'e']; let indices = []; let data3 = [...data1]; data1.forEach(token => indices.push(token.source)); data2.forEach(token => { if (!indices.includes(token)) { data3.push({ source: token, foo: 'default' }) } }); console.log(data3); 

不知道是否有任何超级简单的方法可以不自己编写。

 const data = [ {source: 'b', foo: 'bar'}, {source: 'd', foo: 'baz'} ]; const other = ['b', 'c', 'e']; const defaultValue = 'someDefaultValue'; const joined = [...data]; other.forEach(source => { if (!data.some(item => item.source === source)) { joined.push({source, foo: defaultValue}); } }); console.log(joined); 

可以这么说,数组按照您编写的顺序分别称为firstsecond

那么我认为这是解决方案

let result = [...first];
for(let letter of second) {
    let contains = false;
    for(let obj of first) {
        if(obj.source == letter) {
            contains = true;
            result = [...result, obj]
        }
    }
    if(!contains) result = [...result, {source: letter, bar: "defaultValue"}] 
} 
result = result.sort((a, b) => a.source < b.source ? -1 : (a.source == b.source ? 0 : 1));
result = Array.from(new Set(result))

我不想太复杂,也不想使用较新的语法,但是这里有更多的辅助功能,供人们使用和扩展。 希望足够直接。 这仅对字符串值正确排序。 它是不可变的,因此不会更改原始数组。

 var arrLeft = [ {source: 'b', foo: 'bar'}, {source:'d', foo: 'baz'} ]; var arrRight = ['b', 'c', 'e']; var fullJoin = function(arr1, arr2, key, defaultVal) { // Create deep copy array of the left side to include all... var newArr = JSON.parse(JSON.stringify(arr1)); var newEle; // Loop through right side to make sure to include all for (var i = 0; i < arr2.length; i++) { // If it doesn't exist in left side, add it with default value if (!newArr.find(function(p) { return p[key] === arr2[i]; })) { newEle = {}; newEle[key] = arr2[i]; newEle.foo = defaultVal; newArr.push(newEle); } } // Sort on key alphabetically return newArr.sort(function(a, b) { var sourceA = a[key].toLowerCase(); var sourceB = b[key].toLowerCase(); switch(true) { case sourceA > sourceB: return 1; case sourceA < sourceB: return -1; default: return 0; } }); }; // Call like so console.log(fullJoin(arrLeft,arrRight,'source','somedefault')); 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM