export interface ILocationService { matches ( currentLocation , checkedLocation ) : boolean ; matchesSome ( currentLocation , checkedLocations ) : boolean ; encode ( decoded ) : string [ ] ; decode ( encoded ) : string [ ] [ ] ; } export class LocationService implements ILocationService { public matches ( currentLocation , checkedLocation ) { const highestSpecificityTypes = this . filterForHighestSpecificity ( currentLocation ) . map ( ( type ) => [ ... this . decode ( [ type ] ) [ 0 ] ] ) ; return highestSpecificityTypes . some ( ( location ) => checkedLocation . startsWith ( this . encode ( [ location ] ) [ 0 ] ) ) ; } public matchesSome ( currentLocation , checkedLocations ) { const highestSpecificityTypes = this . filterForHighestSpecificity ( currentLocation ) . map ( ( type ) => [ ... this . decode ( [ type ] ) [ 0 ] ] ) ; return highestSpecificityTypes . some ( ( location ) => checkedLocations . some ( ( checkedLocation ) => checkedLocation . startsWith ( this . encode ( [ location ] ) [ 0 ] ) ) ) ; } public encode ( decoded ) { return decoded . map ( ( group ) => group . join ( ';' ) ) ; } public decode ( encoded ) { return encoded . map ( ( item ) => item . split ( ';' ) ) ; } private filterForHighestSpecificity ( types ) { const decodedTypes = types . map ( ( type ) => [ ... this . decode ( [ type ] ) [ 0 ] ] ) ; return this . encode ( decodedTypes . filter ( ( type ) => ! decodedTypes . some ( ( nestedType ) => nestedType [ 0 ] === type [ 0 ] && type . length < nestedType . length ) ) ) ; } }