@@ -285,9 +285,12 @@ function _deepEqual(actual, expected, strict, memos) {
285285 return areEq ;
286286}
287287
288- function setHasSimilarElement ( set , val1 , strict , memo ) {
289- if ( set . has ( val1 ) )
288+ function setHasSimilarElement ( set , val1 , usedEntries , strict , memo ) {
289+ if ( set . has ( val1 ) ) {
290+ if ( usedEntries )
291+ usedEntries . add ( val1 ) ;
290292 return true ;
293+ }
291294
292295 // In strict mode the only things which can match a primitive or a function
293296 // will already be detected by set.has(val1).
@@ -296,8 +299,14 @@ function setHasSimilarElement(set, val1, strict, memo) {
296299
297300 // Otherwise go looking.
298301 for ( const val2 of set ) {
299- if ( _deepEqual ( val1 , val2 , strict , memo ) )
302+ if ( usedEntries && usedEntries . has ( val2 ) )
303+ continue ;
304+
305+ if ( _deepEqual ( val1 , val2 , strict , memo ) ) {
306+ if ( usedEntries )
307+ usedEntries . add ( val2 ) ;
300308 return true ;
309+ }
301310 }
302311
303312 return false ;
@@ -314,21 +323,33 @@ function setEquiv(a, b, strict, memo) {
314323 if ( a . size !== b . size )
315324 return false ;
316325
326+ // This is a set of the entries in b which have been consumed in our pairwise
327+ // comparison.
328+ //
329+ // When the sets contain only value types (eg, lots of numbers), and we're in
330+ // strict mode, we don't need to match off the entries in a pairwise way. In
331+ // that case this initialization is done lazily to avoid the allocation &
332+ // bookkeeping cost. Unfortunately, we can't get away with that in non-strict
333+ // mode.
334+ let usedEntries = null ;
335+
317336 for ( const val1 of a ) {
337+ if ( usedEntries == null && ( ! strict || typeof val1 === 'object' ) )
338+ usedEntries = new Set ( ) ;
339+
318340 // If the value doesn't exist in the second set by reference, and its an
319341 // object or an array we'll need to go hunting for something thats
320342 // deep-equal to it. Note that this is O(n^2) complexity, and will get
321343 // slower if large, very similar sets / maps are nested inside.
322344 // Unfortunately there's no real way around this.
323- if ( ! setHasSimilarElement ( b , val1 , strict , memo ) ) {
345+ if ( ! setHasSimilarElement ( b , val1 , usedEntries , strict , memo ) )
324346 return false ;
325- }
326347 }
327348
328349 return true ;
329350}
330351
331- function mapHasSimilarEntry ( map , key1 , item1 , strict , memo ) {
352+ function mapHasSimilarEntry ( map , key1 , item1 , usedEntries , strict , memo ) {
332353 // To be able to handle cases like:
333354 // Map([[1, 'a'], ['1', 'b']]) vs Map([['1', 'a'], [1, 'b']])
334355 // or:
@@ -338,8 +359,11 @@ function mapHasSimilarEntry(map, key1, item1, strict, memo) {
338359 // This check is not strictly necessary. The loop performs this check, but
339360 // doing it here improves performance of the common case when reference-equal
340361 // keys exist (which includes all primitive-valued keys).
341- if ( map . has ( key1 ) && _deepEqual ( item1 , map . get ( key1 ) , strict , memo ) )
362+ if ( map . has ( key1 ) && _deepEqual ( item1 , map . get ( key1 ) , strict , memo ) ) {
363+ if ( usedEntries )
364+ usedEntries . add ( key1 ) ;
342365 return true ;
366+ }
343367
344368 if ( strict && ( util . isPrimitive ( key1 ) || util . isFunction ( key1 ) ) )
345369 return false ;
@@ -349,8 +373,13 @@ function mapHasSimilarEntry(map, key1, item1, strict, memo) {
349373 if ( key2 === key1 )
350374 continue ;
351375
376+ if ( usedEntries && usedEntries . has ( key2 ) )
377+ continue ;
378+
352379 if ( _deepEqual ( key1 , key2 , strict , memo ) &&
353380 _deepEqual ( item1 , item2 , strict , memo ) ) {
381+ if ( usedEntries )
382+ usedEntries . add ( key2 ) ;
354383 return true ;
355384 }
356385 }
@@ -366,10 +395,15 @@ function mapEquiv(a, b, strict, memo) {
366395 if ( a . size !== b . size )
367396 return false ;
368397
398+ let usedEntries = null ;
399+
369400 for ( const [ key1 , item1 ] of a ) {
401+ if ( usedEntries == null && ( ! strict || typeof key1 === 'object' ) )
402+ usedEntries = new Set ( ) ;
403+
370404 // Just like setEquiv above, this hunt makes this function O(n^2) when
371405 // using objects and lists as keys
372- if ( ! mapHasSimilarEntry ( b , key1 , item1 , strict , memo ) )
406+ if ( ! mapHasSimilarEntry ( b , key1 , item1 , usedEntries , strict , memo ) )
373407 return false ;
374408 }
375409
0 commit comments