@@ -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