import _ from 'lodash' ; export interface AddElementsToSequentialArrayOptions < T > { getKey ? ( element ) : unknown ; isAfterAnchor ? ( element ) : boolean ; isBeforeAnchor ? ( element ) : boolean ; replace ? : boolean ; } export function addElementsToSequentialArray < T > ( array , elements , { getKey = ( element ) : T => element , isAfterAnchor , isBeforeAnchor , replace : toReplace = false , } = { } ) { let existingKeyToElementMap = new Map ( array . map ( element => [ getKey ( element ) , element ] ) ) ; let newElements = [ ] ; for ( let element of elements ) { let key = getKey ( element ) ; if ( existingKeyToElementMap . has ( key ) ) { if ( toReplace ) { existingKeyToElementMap . set ( key , element ) ; } } else { newElements . push ( element ) ; } } let existingElements = Array . from ( existingKeyToElementMap . values ( ) ) ; if ( newElements . length === 0 ) { return existingElements ; } let afterHitIndex = - 1 ; let beforeHitIndex = - 1 ; for ( let [ index , element ] of existingElements . entries ( ) ) { if ( isAfterAnchor && ( afterHitIndex < 0 || index - afterHitIndex === 1 ) ) { let afterHit = isAfterAnchor ( element ) ; if ( afterHit ) { afterHitIndex = index ; } else if ( afterHitIndex >= 0 ) { break ; } } if ( isBeforeAnchor && beforeHitIndex < 0 ) { let beforeHit = isBeforeAnchor ( element ) ; if ( beforeHit ) { beforeHitIndex = index ; break ; } } } if ( afterHitIndex >= 0 ) { let matchingElements = [ ] ; let remainingNewElements = [ ... newElements ] ; let remainingAfterAnchorElements = existingElements . slice ( afterHitIndex + 1 ) ; let candidateElementsArray = [ remainingNewElements , remainingAfterAnchorElements , ] ; outer : for ( let element of elements ) { let key = getKey ( element ) ; for ( let candidateElements of candidateElementsArray ) { if ( candidateElements . length > 0 && getKey ( candidateElements [ 0 ] ) === key ) { matchingElements . push ( candidateElements . shift ( ) ! ) ; continue outer ; } } break ; } return [ ... existingElements . slice ( 0 , afterHitIndex + 1 ) , ... matchingElements , ... remainingNewElements , ... remainingAfterAnchorElements , ] ; } else if ( beforeHitIndex >= 0 ) { array . splice ( beforeHitIndex , 0 , ... newElements ) ; return [ ... existingElements . slice ( 0 , beforeHitIndex ) , ... newElements , ... existingElements . slice ( beforeHitIndex ) , ] ; } else { return [ ... existingElements , ... newElements ] ; } }