检查两个数组是否具有具有相同属性的对象

Check if two arrays have an object with the same property

提问人:InterstellarX 提问时间:4/21/2022 最后编辑:customcommanderInterstellarX 更新时间:4/23/2022 访问量:3366

问:

我的目标是:比较两个对象,找出是否有 1 个或多个共同点。如果有 1 个或多个共同点,则返回 否则(没有共同点),返回 。truefalse

当前问题: 我正在尝试将该方法与 API 中的 1 个对象和 1 个本地对象一起使用,但有点困惑为什么它不起作用......有什么想法吗?它应该返回 true,因为 John 在两个对象中,但它返回 false 🙃.some()

代码示例:在此示例中,它应该返回,因为 John 是一个既是对象 1 (result1) 又是对象 2 (result 2) 的名称。但是,它返回 .truefalse

有没有人能够帮助我理解我在这里做错了什么?

var result1 = [
        {id:1, name:'Sandra', type:'user', username:'sandra'},
        {id:2, name:'John', type:'admin', username:'johnny2'},
        {id:3, name:'Peter', type:'user', username:'pete'},
        {id:4, name:'Bobby', type:'user', username:'be_bob'}
    ];

var result2 = [
        {id:2, name:'John', email:'[email protected]'},
        {id:4, name:'Bobby', email:'[email protected]'}
    ];

const hasSimilarElement = result1.some((item) => item.name === result2.name); 

console.log(hasSimilarElement);

JavaScript 数组 算法 迭代比较

评论

3赞 Bravo 4/21/2022
这些都不是JSON,没有JSON对象这样的东西 - JSON是一个字符串 - result2是一个数组,不能等于“John”...result2[0].name 是 John ...您需要遍历两个数组以检查是否存在匹配项 - 也许result2.nameresult1.some((item) => result2.some(item2 => item.name === item2.name));
0赞 InterstellarX 4/21/2022
我的意思是好的,但你能帮我理解为什么这个值是假的吗?或者你能帮我理解为什么它没有按照我想要的方式迭代@Bravo吗?.some()
2赞 Bravo 4/21/2022
我确实告诉过你为什么...... 不是“约翰”......它从来都不是字符串,这是因为,除非你设置了一个对 Array 调用的属性,否则它没有这样的属性 - 你只迭代 result1,你没有迭代 result2 - 所有这些都在第一个注释中说result2.nameundefinedname
0赞 Peter Seliger 4/21/2022
OP 需要找到一个值相等的项 within ,而不是直接比较一个不存在的(因此)属性。item.nameresult1result2undefinednameresult2
0赞 Peter Seliger 4/22/2022
@InterstellarX......关于目前提供的答案/方法/解决方案,还有什么问题吗?

答:

0赞 Peter Seliger 4/21/2022 #1

从上面的评论...

“OP 需要在 result2 中找到具有相等 item.nameresult1 的项目,而不是直接比较 result2 的不存在(因此未定义的名称属性。”

或者,正如 3limin4t0r 正确建议的那样,一个人可以再次将 find 换成另一个。

const result1 = [
  { id: 1, name: 'Sandra', type: 'user', username: 'sandra' },
  { id: 2, name: 'John', type: 'admin', username: 'johnny2' },
  { id: 3, name: 'Peter', type: 'user', username: 'pete' },
  { id: 4, name: 'Bobby', type: 'user', username: 'be_bob' },
];
const result2 = [
  { id: 5, name: 'Lea', email: '[email protected]' },
  { id: 2, name: 'John', email: '[email protected]' },
];

const result3 = [
  { id: 5, name: 'Lea', email: '[email protected]' },
];
const result4 = [];

console.log(
  'similar elements within `result1` and `result2` ..?',
  result1.some(itemA =>
    result2.some(itemB => itemB.name === itemA.name)
  )
);
console.log(
  'similar elements within `result1` and `result3` ..?',
  result1.some(itemA =>
    result3.some(itemB => itemB.name === itemA.name)
  )
);
console.log(
  'similar elements within `result1` and `result4` ..?',
  result1.some(itemA =>
    result4.some(itemB => itemB.name === itemA.name)
  )
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

在下一次迭代中,原因之一可能会提出一个通用实现的函数,该函数允许自由选择被比较的属性名称。

实现函数还提供了摆脱两个嵌套迭代的机会(尽管原因提前退出以进行成功的比较)。somesome

在这里,人们将从较短的数组构建一个(属性名称)特定的值索引(单个迭代任务),其中,在迭代较长的数组时,将查找(不迭代)项目相似性的特定值。keykey

function hasSimilarItemsByKey(key, arrA, arrB) {
  const [
    // the shorter array will be the base
    // for a `key` specific value index.
    lookupArray,
    // the longer array will be iterated
    // and have its item `key` specific 
    // values looked up at a value index.
    targetArray,
  ] = [
    arrA,
    arrB,
  ].sort((a, b) => a.length - b.length);

  // create the object based value index
  // (from the shorter array).
  const valueIndex = lookupArray
    .reduce((lookup, item) => {

      const value = item[key];
      lookup[value] ??= value;

      return lookup;
    }, Object.create(null));  // A `prototype`-less object. Thus,
                              // using the `in` operator is safe.
  return targetArray.some(item => item[key] in valueIndex);
}

const result1 = [
  { id: 1, name: 'Sandra', type: 'user', username: 'sandra' },
  { id: 2, name: 'John', type: 'admin', username: 'johnny2' },
  { id: 3, name: 'Peter', type: 'user', username: 'pete' },
  { id: 4, name: 'Bobby', type: 'user', username: 'be_bob' },
];
const result2 = [
  { id: 5, name: 'Lea', email: '[email protected]' },
  { id: 2, name: 'John', email: '[email protected]' },
];

const result3 = [
  // Attention,
  // A matching `id` and a non matching `name`.
  { id: 1, name: 'Lea', email: '[email protected]' },
];
const result4 = [];

console.log(
  'similar elements within `result1` and `result2`\n',
  '...by `name`..?', hasSimilarItemsByKey('name', result1, result2),
  '...by `id`..?', hasSimilarItemsByKey('id', result1, result2),
);
console.log(
  'similar elements within `result1` and `result3`\n',
  '...by `name`..?', hasSimilarItemsByKey('name', result1, result3),
  '...by `id`..?', hasSimilarItemsByKey('id', result1, result3),
);
console.log(
  'similar elements within `result1` and `result4`\n',
  '...by `name`..?', hasSimilarItemsByKey('name', result1, result4),
  '...by `id`..?', hasSimilarItemsByKey('id', result1, result4),
);
.as-console-wrapper { min-height: 100%!important; top: 0; }

评论

0赞 Peter Seliger 4/21/2022
如果除了投票之外,还有关于什么值得投反对票的评论,我有机会改进代码并从错误中吸取教训。
0赞 maximus 4/21/2022 #2

Array.prototype.some() some() 方法测试数组中是否至少有一个元素通过了由提供的函数实现的测试。

在您的示例中,您尝试比较 2 个对象数组。你有 false,因为 result2.name 未定义,你可能需要指定特定元素的索引,例如 result2[0].name

如果您想将 2 个数组与一些数组进行比较,您还需要遍历 result2 项目。你可以像这样使用somehting:

var result1 = [
        {id:1, name:'Sandra', type:'user', username:'sandra'},
        {id:2, name:'John', type:'admin', username:'johnny2'},
        {id:3, name:'Peter', type:'user', username:'pete'},
        {id:4, name:'Bobby', type:'user', username:'be_bob'}
    ];

var result2 = [
        {id:2, name:'John', email:'[email protected]'},
        {id:4, name:'Bobby', email:'[email protected]'}
    ];    
const hasSimilarElement = result2.filter((item1) => !result1.some(item2 => item1.name === item2.name ));

console.log(hasSimilarElement);
0赞 Bravo 4/21/2022 #3

代码失败的原因是您试图在 result1 Array 的元素中找到 a 的匹配项nameresult2.name

但是,也是一个 ,数组没有恰好是与您正在搜索的元素匹配的所有元素 - 这不是它的工作原理result2Array.name.name

您需要遍历两个数组以查找匹配项

var result1 = [
        {id:1, name:'Sandra', type:'user', username:'sandra'},
        {id:2, name:'John', type:'admin', username:'johnny2'},
        {id:3, name:'Peter', type:'user', username:'pete'},
        {id:4, name:'Bobby', type:'user', username:'be_bob'}
    ];

var result2 = [
        {id:2, name:'John', email:'[email protected]'},
        {id:4, name:'Bobby', email:'[email protected]'}
    ];

const hasSimilarElement = result1.some((item) => result2.some(item2 => item.name === item2.name)); 

console.log(hasSimilarElement);
.as-console-wrapper { min-height: 100%!important; top: 0; }

然而,对于非常大的结果集,这是低效的 - 我不是“大 O 符号专家,但我相信这将是 O(n2) - 你可能会比较 * 次result1.lengthresult2.length

对于这个简单的例子,你可以只从两个结果中提取,并创建一个...如果大小小于组合结果 s,则表示存在重复项.nameSetSet.length

在这里,您将对每个结果进行一次迭代 - 如果两个集合都有 1000 个元素,则为 2,000 次迭代,而使用上面的 naïve 方法则为 1,000,000 次

var result1 = [
        {id:1, name:'Sandra', type:'user', username:'sandra'},
        {id:2, name:'John', type:'admin', username:'johnny2'},
        {id:3, name:'Peter', type:'user', username:'pete'},
        {id:4, name:'Bobby', type:'user', username:'be_bob'}
    ];

var result2 = [
        {id:2, name:'John', email:'[email protected]'},
        {id:4, name:'Bobby', email:'[email protected]'}
    ];

const hasSimilarElement = new Set([...result1, ...result2].map(({name}) => name)).size < result1.length + result2.length
console.log(hasSimilarElement);
.as-console-wrapper { min-height: 100%!important; top: 0; }

但是,如果其中一个结果中有重复的名称,则存在缺陷,这将无法按预期工作

在这里,每个集合迭代两次 - 但是,给定 2 x 1,000 个长结果,4,000 次迭代仍然优于 1,000,000 次

var result1 = [
        {id:1, name:'Sandra', type:'user', username:'sandra'},
        {id:2, name:'John', type:'admin', username:'johnny2'},
        {id:3, name:'Peter', type:'user', username:'pete'},
        {id:4, name:'Bobby', type:'user', username:'be_bob'}
    ];

var result2 = [
        {id:2, name:'John', email:'[email protected]'},
        {id:4, name:'Bobby', email:'[email protected]'}
    ];

const set1 = new Set(result1.map(({name}) => name));
const set2 = new Set(result2.map(({name}) => name));
const both = new Set([...set1, ...set2]);
const hasSimilarElement = both.size < set1.size + set2.size;

console.log(hasSimilarElement);
.as-console-wrapper { min-height: 100%!important; top: 0; }

评论

2赞 3limin4t0r 4/21/2022
我觉得你把布景的事情搞得太复杂了。您可以只使用一个集合,并将其与 . 结合使用。例如。 有了这个,您就不会有重复名称的问题,也不需要 和 .some()const hasSimilarElement = result1.some(({name}) => set2.has(name))set1both
0赞 Bravo 4/21/2022
@3limin4t0r - 你可能是对的 - 最后一段代码是事后的想法,我真的没有想太多
5赞 customcommander 4/21/2022 #4

您可以创建一个函数,该函数将一个函数用于查找,然后是两个数组 &:fxsys

const checkBy = f => (xs, ys) => {
  let [long, short] = xs.length > ys.length ? [xs, ys] : [ys, xs];
  short = new Set(short.map(f));
  return long.some(x => short.has(f(x)));
};

然后:

const checkByName = checkBy(x => x.name);

checkByName( [ {id: 1, name:'Sandra', type:  'user', username:  'sandra'}
             , {id: 2, name:  'John', type: 'admin', username: 'johnny2'}
             , {id: 3, name: 'Peter', type:  'user', username:    'pete'}
             , {id: 4, name: 'Bobby', type:  'user', username:  'be_bob'}]

           , [ {id: 2, name:  'John', email: '[email protected]'}
             , {id: 4, name: 'Bobby', email:  '[email protected]'}]);
//=> true

checkByName( [ {id: 1, name:'Sandra', type:  'user', username:  'sandra'}
             , {id: 2, name:  'John', type: 'admin', username: 'johnny2'}
             , {id: 3, name: 'Peter', type:  'user', username:    'pete'}
             , {id: 4, name: 'Bobby', type:  'user', username:  'be_bob'}]

           , [ {id: 2, name:  'xxxx', email: '[email protected]'}
             , {id: 4, name: 'yyyyy', email:  '[email protected]'}]);
//=> false

评论

0赞 Peter Seliger 4/21/2022
...恕我直言,从技术上讲,这是迄今为止最好的方法/实现。
0赞 Scott Sauyet 4/23/2022
这很好,但与更简单的列表相比,唯一的优点是它可以映射到较短的列表。对于许多用途,我不会为这种优化而烦恼。const overlap = (f) => (xs, ys, ks = new Set (xs .map (f))) => ys .some (y => ks .has (f (y)))f
2赞 Scott Sauyet 4/23/2022 #5

我会将 customcommander 建议的技术用于您的确切场景,以及您可以从对象中提取唯一键作为基元类型的任何位置。(这里是字符串,所以可以工作。name

如果你需要更通用的东西,那么你可以提供一个函数来判断两个元素是否相等,并像这样使用它:

const overlap = (equal) => (xs, ys) => 
  xs .some (x => ys .some (y => equal (x, y)))

const result1 = [{id:1, name:'Sandra', type:'user', username:'sandra'}, {id:2, name:'John', type:'admin', username:'johnny2'}, {id:3, name:'Peter', type:'user', username:'pete'}, {id:4, name:'Bobby', type:'user', username:'be_bob'}]
const result2 = [{id:2, name:'John', email:'[email protected]'}, {id:4, name:'Bobby', email:'[email protected]'}]

console .log (overlap ((x, y) => x .name == y .name) (result1, result2))

在可以生成唯一键的情况下,效率较低,但它更通用。您甚至可以将它用于两个数组,其中两个数组具有具有不同结构的对象,例如传递 。(x, y) => x .id == y .primaryKey

评论

0赞 customcommander 4/23/2022
用户提供的相等函数绝对是正确的方法。不知道为什么我没有想到这一点。
1赞 Mulan 4/23/2022
完美答案 💯