javascript - 两个包含对象的数组的差分和交集

标签 javascript arrays set-intersection set-difference set-operations

我有两个数组 list1list2,它们的对象具有某些属性; userId 是 Id 或唯一属性:

list1 = [
    { userId: 1234, userName: 'XYZ'  }, 
    { userId: 1235, userName: 'ABC'  }, 
    { userId: 1236, userName: 'IJKL' },
    { userId: 1237, userName: 'WXYZ' }, 
    { userId: 1238, userName: 'LMNO' }
]

list2 = [
    { userId: 1235, userName: 'ABC'  },  
    { userId: 1236, userName: 'IJKL' },
    { userId: 1252, userName: 'AAAA' }
]

我正在寻找一种简单的方法来执行以下三个操作:

  1. list1操作list2应该返回元素的交集:

    [
        { userId: 1235, userName: 'ABC'  },
        { userId: 1236, userName: 'IJKL' }
    ]
    
  2. list1 operation list2 应该返回 list1 中没有出现在 list2 中的所有元素的列表:

    [
        { userId: 1234, userName: 'XYZ'  },
        { userId: 1237, userName: 'WXYZ' }, 
        { userId: 1238, userName: 'LMNO' }
    ]
    
  3. list2 操作 list1 应该返回 list2 中没有出现在 list1 中的元素列表:

    [
        { userId: 1252, userName: 'AAAA' }
    ]
    

最佳答案

您可以定义三个函数 inBothinFirstOnlyinSecondOnly,它们都将两个列表作为参数,并尽可能返回一个列表从函数名来理解。主要逻辑可以放在一个共同的函数 operation 中,这三个函数都依赖。

这里有一些可供选择的操作的实现,您可以在下面找到一个片段:

  • 普通的旧 JavaScript for 循环
  • 使用filter一些 数组方法的箭头函数
  • 使用 Set 优化查找

普通的 for 循环

// Generic helper function that can be used for the three operations:        
function operation(list1, list2, isUnion) {
    var result = [];
    
    for (var i = 0; i < list1.length; i++) {
        var item1 = list1[i],
            found = false;
        for (var j = 0; j < list2.length && !found; j++) {
            found = item1.userId === list2[j].userId;
        }
        if (found === !!isUnion) { // isUnion is coerced to boolean
            result.push(item1);
        }
    }
    return result;
}

// Following functions are to be used:
function inBoth(list1, list2) {
    return operation(list1, list2, true);
}

function inFirstOnly(list1, list2) {
    return operation(list1, list2);
}

function inSecondOnly(list1, list2) {
    return inFirstOnly(list2, list1);
}

// Sample data
var list1 = [
    { userId: 1234, userName: 'XYZ'  }, 
    { userId: 1235, userName: 'ABC'  }, 
    { userId: 1236, userName: 'IJKL' },
    { userId: 1237, userName: 'WXYZ' }, 
    { userId: 1238, userName: 'LMNO' }
];
var list2 = [
    { userId: 1235, userName: 'ABC'  },  
    { userId: 1236, userName: 'IJKL' },
    { userId: 1252, userName: 'AAAA' }
];
  
console.log('inBoth:', inBoth(list1, list2)); 
console.log('inFirstOnly:', inFirstOnly(list1, list2)); 
console.log('inSecondOnly:', inSecondOnly(list1, list2)); 

使用filter一些 数组方法的箭头函数

这使用了一些 ES5 和 ES6 特性:

// Generic helper function that can be used for the three operations:        
const operation = (list1, list2, isUnion = false) =>
    list1.filter( a => isUnion === list2.some( b => a.userId === b.userId ) );

// Following functions are to be used:
const inBoth = (list1, list2) => operation(list1, list2, true),
      inFirstOnly = operation,
      inSecondOnly = (list1, list2) => inFirstOnly(list2, list1);

// Sample data
const list1 = [
    { userId: 1234, userName: 'XYZ'  }, 
    { userId: 1235, userName: 'ABC'  }, 
    { userId: 1236, userName: 'IJKL' },
    { userId: 1237, userName: 'WXYZ' }, 
    { userId: 1238, userName: 'LMNO' }
];
const list2 = [
    { userId: 1235, userName: 'ABC'  },  
    { userId: 1236, userName: 'IJKL' },
    { userId: 1252, userName: 'AAAA' }
];
  
console.log('inBoth:', inBoth(list1, list2)); 
console.log('inFirstOnly:', inFirstOnly(list1, list2)); 
console.log('inSecondOnly:', inSecondOnly(list1, list2));

优化查找

由于嵌套循环,上述解决方案的时间复杂度为 O(n²) -- some 也表示循环。因此,对于大型数组,您最好在用户 ID 上创建一个(临时)散列。这可以通过提供 Set (ES6) 作为将生成过滤器回调函数的函数的参数来即时完成。然后该函数可以使用 has 在恒定时间内执行查找:

// Generic helper function that can be used for the three operations:        
const operation = (list1, list2, isUnion = false) =>
    list1.filter(
        (set => a => isUnion === set.has(a.userId))(new Set(list2.map(b => b.userId)))
    );

// Following functions are to be used:
const inBoth = (list1, list2) => operation(list1, list2, true),
      inFirstOnly = operation,
      inSecondOnly = (list1, list2) => inFirstOnly(list2, list1);

// Sample data
const list1 = [
    { userId: 1234, userName: 'XYZ'  }, 
    { userId: 1235, userName: 'ABC'  }, 
    { userId: 1236, userName: 'IJKL' },
    { userId: 1237, userName: 'WXYZ' }, 
    { userId: 1238, userName: 'LMNO' }
];
const list2 = [
    { userId: 1235, userName: 'ABC'  },  
    { userId: 1236, userName: 'IJKL' },
    { userId: 1252, userName: 'AAAA' }
];
  
console.log('inBoth:', inBoth(list1, list2)); 
console.log('inFirstOnly:', inFirstOnly(list1, list2)); 
console.log('inSecondOnly:', inSecondOnly(list1, list2));

关于javascript - 两个包含对象的数组的差分和交集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33356504/

相关文章:

javascript - JSON 文件中所有进程的 PM2 通用设置

arrays - 数组元素的快速返回索引

arrays - 有没有一种方便的方法可以将查找表应用于 numpy 中的大型数组?

javascript - jQuery token 输入 : Tokens are not getting added

javascript - angularJS 表单数据到 ActionResult - ASP .NET MVC

javascript - 无法将 html 内容发送到 Web 服务,出现未找到错误

algorithm - 高效列表交集算法

c - 构建的正确返回类型是什么?

sql - 试图了解sql查询中的 "except all"

python - 检查python中两个字符串之间的交集