javascript中数组交集的最简单代码
在 javascript 中实现数组相交的最简单的、无库的代码是什么?我想写
intersection([1,2,3], [2,3,4,5])
并得到
[2, 3]
What's the simplest, library-free code for implementing array intersections in javascript? I want to write
intersection([1,2,3], [2,3,4,5])
and get
[2, 3]
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(30)
该函数利用了字典的强大功能,避免了 N^2 问题。每个数组仅循环一次,第三次或更短的循环将返回最终结果。
它还支持数字、字符串和对象。
如果有特殊情况无法解决,只需修改
generateStrKey
函数,一定可以解决。该函数的技巧在于它根据类型和值唯一地表示每个不同的数据。此变体具有一些性能改进。如果任何数组为空,请避免循环。它还首先遍历较短的数组,因此如果它在第二个数组中找到第一个数组的所有值,则退出循环。
This function avoids the N^2 problem, taking advantage of the power of dictionaries. Loops through each array only once, and a third and shorter loop to return the final result.
It also supports numbers, strings, and objects.
If there is a special case that cannot be solved, just by modifying the
generateStrKey
function, it could surely be solved. The trick of this function is that it uniquely represents each different data according to type and value.This variant has some performance improvements. Avoid loops in case any array is empty. It also starts by walking through the shorter array first, so if it finds all the values of the first array in the second array, exits the loop.
这是 underscore.js 实现:
来源:http://underscorejs.org/docs/underscore.html#section-62
Here is underscore.js implementation:
Source: http://underscorejs.org/docs/underscore.html#section-62
使用一个数组创建一个对象,然后循环访问第二个数组以检查该值是否作为键存在。
Create an Object using one array and loop through the second array to check if the value exists as key.
我认为在内部使用对象可以帮助计算并且也可以提高性能。
// 方法维护每个元素的计数,并且也适用于负元素
I think using an object internally can help with computations and could be performant too.
// Approach maintains a count of each element and works for negative elements too
我正在使用地图,甚至可以使用对象。
I am using map even object could be used.
这是提议的标准:当前第 2 阶段提案 https://github.com/tc39/ proposal-set-methods,你可以使用
在那之前,你可以使用 Immutable.js'
Set
,这激发了该提案This is a proposed standard: With the currently stage 2 proposal https://github.com/tc39/proposal-set-methods, you could use
Until then, you could use Immutable.js's
Set
, which inspired that proposal我扩展了 tarulen 的答案以处理任意数量的数组。它还应该适用于非整数值。
I extended tarulen's answer to work with any number of arrays. It also should work with non-integer values.
使用
Array.prototype 的组合。过滤器
和< code>Array.prototype.includes:对于较旧的浏览器,使用
Array.prototype.indexOf
并且没有箭头函数:注意!
.includes
和.indexOf
在内部都使用===
比较数组中的元素,因此如果数组包含对象,它只会比较对象参考文献(不是其内容)。如果您想指定自己的比较逻辑,请使用 改为Array.prototype.some
。Use a combination of
Array.prototype.filter
andArray.prototype.includes
:For older browsers, with
Array.prototype.indexOf
and without an arrow function:NB! Both
.includes
and.indexOf
internally compares elements in the array by using===
, so if the array contains objects it will only compare object references (not their content). If you want to specify your own comparison logic, useArray.prototype.some
instead.破坏性似乎最简单,特别是如果我们可以假设输入已排序:
非破坏性必须更复杂,因为我们必须跟踪索引:
Destructive seems simplest, especially if we can assume the input is sorted:
Non-destructive has to be a hair more complicated, since we’ve got to track indices:
如果您的环境支持ECMAScript 6 Set,一个简单的据称有效的(参见规范链接)方式:
更短,但可读性较差(也无需创建额外的交集
Set
):请注意,使用集合时,您只会获得不同的值,因此
new Set ([1, 2, 3, 3]).size
的计算结果为3
。If your environment supports ECMAScript 6 Set, one simple and supposedly efficient (see specification link) way:
Shorter, but less readable (also without creating the additional intersection
Set
):Note that when using sets you will only get distinct values, thus
new Set([1, 2, 3, 3]).size
evaluates to3
.使用 Underscore.js 或 lodash.js
Using Underscore.js or lodash.js
我推荐上面的简洁解决方案,它在大输入方面优于其他实现。如果小输入的性能很重要,请检查以下替代方案。
替代方案和性能比较:
请参阅以下代码片段以了解替代实现并检查https:// jsperf.com/array-intersection-comparison 进行性能比较。
Firefox 53 中的结果:
大型数组(10,000 个元素)上的每秒操作数:
小型数组(100 个元素)上的每秒操作数:
I recommend above succinct solution which outperforms other implementations on large inputs. If performance on small inputs matters, check the alternatives below.
Alternatives and performance comparison:
See the following snippet for alternative implementations and check https://jsperf.com/array-intersection-comparison for performance comparisons.
Results in Firefox 53:
Ops/sec on large arrays (10,000 elements):
Ops/sec on small arrays (100 elements):
最简单、最快的O(n)和最短路线:
@nbarbosa 的答案几乎相同,但他将两个数组转换为
Set
,然后返回array
。不同之处在于,他的代码将仅返回唯一记录,而此代码的结果还将包含数组b
中的重复条目(假设两个数组都没有填充唯一值)。因此,请使用适合您要求的代码
Simplest, fastest O(n) and shortest way:
@nbarbosa has almost the same answer but he cast both arrays to
Set
and then back toarray
. Difference is that his code will return only unique records while result of this code will also contain repeated entries from arrayb
(assuming that both arrays aren't filled with unique values).So use which ever code fits your requirements
我在 ES6 方面的贡献。一般来说,它找到一个数组与作为参数提供的不定数量的数组的交集。
My contribution in ES6 terms. In general it finds the intersection of an array with indefinite number of arrays provided as arguments.
只使用关联数组怎么样?
编辑:
How about just using associative arrays?
edit:
如果您需要让它处理多个数组的相交:
If you need to have it handle intersecting multiple arrays:
@atk 对原语排序数组的实现的性能可以通过使用 .pop 而不是 .shift 来提高。
我使用 jsPerf 创建了一个 基准。使用 .pop 的速度大约快三倍。
The performance of @atk's implementation for sorted arrays of primitives can be improved by using .pop rather than .shift.
I created a benchmark using jsPerf. It's about three times faster to use .pop.
像这样的东西,虽然没有经过很好的测试。
PS:该算法仅适用于数字和普通字符串,任意对象数组的交集可能不起作用。
Something like this, Not tested well though.
PS:The algorithm only intended for Numbers and Normal Strings, intersection of arbitary object arrays may not work.
使用jQuery:
Using jQuery:
对此处最小的一个进行微小的调整(filter/indexOf 解决方案),即创建以下值之一的索引使用 JavaScript 对象的数组,会将其从 O(N*M) 减少到“可能”线性时间。 来源1 来源2
这不是最简单的解决方案(它的代码比 filter+indexOf 多),也不是最快的(可能是比 intersect_safe() 慢一个常数因子,但似乎是一个很好的平衡。它非常简单,同时提供良好的性能,并且不需要预先排序的输入。
A tiny tweak to the smallest one here (the filter/indexOf solution), namely creating an index of the values in one of the arrays using a JavaScript object, will reduce it from O(N*M) to "probably" linear time. source1 source2
This isn't the very simplest solution (it's more code than filter+indexOf), nor is it the very fastest (probably slower by a constant factor than intersect_safe()), but seems like a pretty good balance. It is on the very simple side, while providing good performance, and it doesn't require pre-sorted inputs.
对于仅包含字符串或数字的数组,您可以按照其他一些答案进行排序。对于任意对象数组的一般情况,我认为您无法避免这样做。下面将为您提供作为
arrayIntersection
参数提供的任意数量数组的交集:For arrays containing only strings or numbers you can do something with sorting, as per some of the other answers. For the general case of arrays of arbitrary objects I don't think you can avoid doing it the long way. The following will give you the intersection of any number of arrays provided as parameters to
arrayIntersection
:另一种索引方法能够一次处理任意数量的数组:
它仅适用于可以作为字符串计算的值,您应该将它们作为数组传递,例如:
...但它透明地接受对象作为参数或任何元素相交(始终返回公共值数组)。示例:
编辑:我刚刚注意到这在某种程度上有点错误。
也就是说:我对其进行编码时认为输入数组本身不能包含重复项(所提供的示例不包含重复项)。
但如果输入数组碰巧包含重复项,就会产生错误的结果。示例(使用下面的实现):
幸运的是,只需添加二级索引即可轻松解决此问题。即:
Change:
by:
...and:
by:
完整示例:
Another indexed approach able to process any number of arrays at once:
It works only for values that can be evaluated as strings and you should pass them as an array like:
...but it transparently accepts objects as parameter or as any of the elements to be intersected (always returning array of common values). Examples:
EDIT: I just noticed that this is, in a way, slightly buggy.
That is: I coded it thinking that input arrays cannot itself contain repetitions (as provided example doesn't).
But if input arrays happen to contain repetitions, that would produce wrong results. Example (using below implementation):
Fortunately this is easy to fix by simply adding second level indexing. That is:
Change:
by:
...and:
by:
Complete example:
通过对数据进行一些限制,您可以在线性时间内完成!
对于正整数:使用将值映射到“已见/未见”布尔值的数组。
对于对象也有类似的技术:采用一个虚拟键,为 array1 中的每个元素将其设置为“true”,然后在 array2 的元素中查找该键。完成后清理干净。
当然,您需要确保该密钥之前没有出现过,否则您将破坏您的数据......
With some restrictions on your data, you can do it in linear time!
For positive integers: use an array mapping the values to a "seen/not seen" boolean.
There is a similar technique for objects: take a dummy key, set it to "true" for each element in array1, then look for this key in elements of array2. Clean up when you're done.
Of course you need to be sure the key didn't appear before, otherwise you'll be destroying your data...
您现在可以使用新添加的内置
intersection
方法:这是 MDN 上的文档。
You can now use the freshly added built-in
intersection
method:Here is the documentation for it on MDN .
为了简单起见:
优点:
缺点:
你不会想将其用于3D引擎或内核工作,但如果你在使用它时遇到问题在基于事件的应用程序中运行,您的设计存在更大的问题。
For simplicity:
Benefits:
Drawbacks:
You wouldn't want to use this for 3D engine or kernel work, but if you have problems getting this to run in an event-based app, your design has bigger problems.
我编写了一个交集函数,它甚至可以根据对象的特定属性来检测对象数组的交集。
例如,
我们想要基于
id
属性进行交集,那么输出应该是:因此,相同的函数(注意:ES6 代码)是:
并且您可以将该函数调用为:
另请注意:考虑到第一个数组是主数组,该函数会查找交集,因此交集结果将是主数组的结果。
I have written an intesection function which can even detect intersection of array of objects based on particular property of those objects.
For instance,
and we want intersection based on the
id
property, then the output should be :As such, the function for the same (note: ES6 code) is :
and you can call the function as:
Also note: this function finds intersection considering the first array is the primary array and thus the intersection result will be that of the primary array.
我将贡献对我来说最有效的东西:
I'll contribute with what has been working out best for me:
ES2015 的函数式方法
函数式方法必须考虑仅使用没有副作用的纯函数,每个函数只涉及一项作业。
这些限制增强了所涉及功能的可组合性和可重用性。
请注意,使用了原生的
Set
类型,它有一个优点查找性能。
避免重复
显然,第一个
Array
中重复出现的项目会被保留,而第二个Array
则会被删除重复项。这可能是也可能不是期望的行为。如果您需要唯一的结果,只需将dedupe
应用于第一个参数:计算任意数量的
Array
的交集如果你想计算任意数量的
Array
的交集,只需将intersect
与组合起来>折叠
。这是一个方便的函数:A functional approach with ES2015
A functional approach must consider using only pure functions without side effects, each of which is only concerned with a single job.
These restrictions enhance the composability and reusability of the functions involved.
Please note that the native
Set
type is used, which has an advantageouslookup performance.
Avoid duplicates
Obviously repeatedly occurring items from the first
Array
are preserved, while the secondArray
is de-duplicated. This may be or may be not the desired behavior. If you need a unique result just applydedupe
to the first argument:Compute the intersection of any number of
Array
sIf you want to compute the intersection of an arbitrarily number of
Array
s just composeintersect
withfoldl
. Here is a convenience function:.reduce
构建地图,.filter
查找交集。.filter
中的delete
允许我们将第二个数组视为唯一的集合。我发现这种方法很容易推理。它以恒定的时间执行。
.reduce
to build a map, and.filter
to find the intersection.delete
within the.filter
allows us to treat the second array as though it's a unique set.I find this approach pretty easy to reason about. It performs in constant time.