export class Utils { private static SingleUrlRegExp = new RegExp ( '^https?:\/\/[^;,]+' ) ; public static isIEOrEdge = /msie\s|trident\/|edg\//i . test ( window . navigator . userAgent ) ; /**
     * checks if two arrays have the same primitives;
     */ public static arraysAreEqual < T > ( list , list2 ) { if ( list === null || list === undefined || list2 === null || list2 === undefined ) { return false ; } return list . every ( entry => list2 . includes ( entry ) ) && list2 . every ( entry => list . includes ( entry ) ) ; } /**
     * Filter all duplicates
     */ public static unique < T > ( list ) { return Array . from ( new Set ( list ) ) ; } public static max ( list ) { return list . reduce ( ( a , b ) => Math . max ( a , b ) ) ; } /**
     * implements lodash groupBy in es6. returns a dictionary of lists
     */ public static groupByFunc < T > ( list , keyFunction ) { return list . reduce ( ( previous , current ) => { const key = keyFunction ( current ) ; if ( key in previous ) { previous [ key ] . push ( current ) ; } else { previous [ key ] = [ current ] ; } return previous ; } , { } ) ; } param key public static groupBy < T > ( list , key ) { return list . reduce ( ( previous , current ) => { previous [ key ] = ( previous [ key ] || [ ] ) . push ( current ) ; return previous ; } , { } ) ; } param key public static keyBy < T > ( list , key ) { return list . reduce ( ( previous , current ) => { previous [ current [ key ] ] = current ; return previous ; } , { } ) ; } param keyFunction public static keyByFromFunction < T > ( list , keyFunction ) { return list . reduce ( ( previous , current ) => { previous [ keyFunction ( current ) ] = current ; return previous ; } , { } ) ; } /**
     * Check if the input value is numeric.
     * Solution comes from http://stackoverflow.com/questions/18082/validate-decimal-numbers-in-javascript-isnumeric
     */ public static isNumeric ( value ) { return ! isNaN ( parseFloat ( value ) ) && isFinite ( value ) ; } /**
     * Extract resolved property from nested object.
     *   e.g. result({ 'a': { 'b': { 'c': 1 } } }, "a.b.c") returns 1
     * Lodash version of result does not work here because b and c might
     * be function where _.result only call function when it is the last
     * element in the chain.
     */ public static result ( item , propertyPath ) { let value = item ; if ( propertyPath !== null ) { propertyPath . split ( '.' ) . forEach ( path => { if ( typeof value [ path ] === 'function' ) { value = value [ path ] ( ) ; } else { value = value [ path ] ; } } ) ; } return value ; } /**
     * Check if a giving object represents a Badge object
     */ public static isBadge ( item ) { return item && item . hasOwnProperty ( 'text' ) && item . hasOwnProperty ( 'badgeId' ) ; } public static injectLink ( textToReplace , replaceText , url , title ) { return textToReplace . replace ( replaceText , `<a title=${ title } ng-href="${ url }" ">${ replaceText }</a>` ) ; } public static isSingleURL ( str ) { return Utils . SingleUrlRegExp . test ( str . toLowerCase ( ) ) ; } public static hexToBytes ( hex ) { const bytes = [ ] ; for ( let c = 0 ; c < hex . length ; c += 2 ) { const value = parseInt ( hex . substr ( c , 2 ) , 16 ) ; if ( ! isNaN ( value ) && value >= 0 ) { bytes . push ( value ) ; } } return bytes ; } public static bytesToHex ( bytes , maxLength = Number . MAX_VALUE ) { const maxBytes = bytes . slice ( 0 , maxLength ) ; const hex = [ ] ; for ( let i = 0 ; i < maxBytes . length ; i ++ ) { hex . push ( ( bytes [ i ] ) . toString ( 16 ) ) ; } return hex . join ( '' ) + ( bytes . length > maxLength ? '...' : '' ) ; } public static getFriendlyFileSize ( fileSizeinBytes ) { let displayFileSize ; const byte = 1 ; const kiloByte = 1024 * byte ; const megaByte = 1024 * kiloByte ; const gigaByte = 1024 * megaByte ; const teraByte = 1024 * gigaByte ; if ( fileSizeinBytes <= kiloByte ) { displayFileSize = fileSizeinBytes + ' Bytes' ; } else if ( fileSizeinBytes < megaByte ) { displayFileSize = ( fileSizeinBytes / kiloByte ) . toFixed ( 2 ) + ' KB' ; } else if ( fileSizeinBytes < gigaByte ) { displayFileSize = ( fileSizeinBytes / megaByte ) . toFixed ( 2 ) + ' MB' ; } else if ( fileSizeinBytes < teraByte ) { displayFileSize = ( fileSizeinBytes / gigaByte ) . toFixed ( 2 ) + ' GB' ; } else { displayFileSize = ( fileSizeinBytes / teraByte ) . toFixed ( 2 ) + ' TB' ; } return displayFileSize ; } public static objectToFormattedText ( obj , depth = 0 ) { let text = '' ; Object . keys ( obj ) . forEach ( key => { const value = obj [ key ] ; if ( typeof value === 'object' ) { text += '' . padStart ( depth ) + `${ key } : \r\n` ; text += Utils . objectToFormattedText ( value , depth + 4 ) ; } else if ( typeof value !== 'function' ) { text += '' . padStart ( depth ) + `${ key } : ${ value }\r\n` ; } } ) ; return text ; } public static addToArrayAndTrim < T > ( list , data , maxLength , onRemoval = ( item ) => null , onAddition = ( item ) => null ) { if ( list . length >= maxLength ) { const r = list . splice ( maxLength - 1 , 1 ) ; onRemoval ( r [ 0 ] ) ; } list . splice ( 0 , 0 , data ) ; onAddition ( data ) ; } } export interface ICounterMostCommonEntry { key : string | number ; value : number ; } export class Counter { private counts = { } ; public add ( key , incrementalValue = 1 ) { if ( this . counts [ key ] === undefined ) { this . counts [ key ] = 0 ; } this . counts [ key ] += incrementalValue ; } public clearAll ( ) { this . counts = { } ; } public mostCommon ( ) { const l = Object . keys ( this . counts ) . map ( key => { return { key , value : this . counts [ key ] } ; } ) ; return l . sort ( this . sortFunction ) ; } private sortFunction ( a , b ) { return a . value > b . value ? - 1 : 1 ; } public entries ( ) { return Object . keys ( this . counts ) . map ( key => { return { key , value : this . counts [ key ] } ; } ) ; } }