function getLength ( type , collection ) { if ( type === 'Object' ) { return Object . keys ( collection as { } ) . length ; } else if ( type === 'Array' ) { return ( collection as unknown [ ] ) . length ; } return Infinity ; } function isIterableMap ( collection ) { return typeof ( collection as Map < any , any > ) . set === 'function' ; } function getEntries ( type , collection , sortObjectKeys ? , from = 0 , to = Infinity ) { let res ; if ( type === 'Object' ) { let keys = Object . getOwnPropertyNames ( collection ) ; if ( sortObjectKeys ) { keys . sort ( sortObjectKeys === true ? undefined : sortObjectKeys ) ; } keys = keys . slice ( from , to + 1 ) ; res = { entries : keys . map ( ( key ) => ( { key , value : collection [ key ] } ) ) , } ; } else if ( type === 'Array' ) { res = { entries : collection . slice ( from , to + 1 ) . map ( ( val , idx ) => ( { key : idx + from , value : val } ) ) , } ; } else { let idx = 0 ; const entries = [ ] ; let done = true ; const isMap = isIterableMap ( collection ) ; for ( const item of collection ) { if ( idx > to ) { done = false ; break ; } if ( from <= idx ) { if ( isMap && Array . isArray ( item ) ) { if ( typeof item [ 0 ] === 'string' || typeof item [ 0 ] === 'number' ) { entries . push ( { key : item [ 0 ] , value : item [ 1 ] } ) ; } else { entries . push ( { key : `[entry ${ idx }]` , value : { '[key]' : item [ 0 ] , '[value]' : item [ 1 ] , } , } ) ; } } else { entries . push ( { key : idx , value : item } ) ; } } idx ++ ; } res = { hasMore : ! done , entries , } ; } return res ; } function getRanges ( from , to , limit ) { const ranges = [ ] ; while ( to - from > limit * limit ) { limit = limit * limit ; } for ( let i = from ; i <= to ; i += limit ) { ranges . push ( { from : i , to : Math . min ( to , i + limit - 1 ) } ) ; } return ranges ; } export default function getCollectionEntries ( type , collection , sortObjectKeys , limit , from = 0 , to = Infinity ) { const getEntriesBound = getEntries . bind ( null , type , collection , sortObjectKeys ) ; if ( ! limit ) { return getEntriesBound ( ) . entries ; } const isSubset = to < Infinity ; const length = Math . min ( to - from , getLength ( type , collection ) ) ; if ( type !== 'Iterable' ) { if ( length <= limit || limit < 7 ) { return getEntriesBound ( from , to ) . entries ; } } else { if ( length <= limit && ! isSubset ) { return getEntriesBound ( from , to ) . entries ; } } let limitedEntries ; if ( type === 'Iterable' ) { const { hasMore , entries } = getEntriesBound ( from , from + limit - 1 ) ; limitedEntries = hasMore ? [ ... entries , ... getRanges ( from + limit , from + 2 * limit - 1 , limit ) ] : entries ; } else { limitedEntries = isSubset ? getRanges ( from , to , limit ) : [ ... getEntriesBound ( 0 , limit - 5 ) . entries , ... getRanges ( limit - 4 , length - 5 , limit ) , ... getEntriesBound ( length - 4 , length - 1 ) . entries , ] ; } return limitedEntries ; }