private getFoodEntries(): Map<number, Set<string>> { // Reduce over the second through last line, converting the string into a // Map of {price: ["set", "of", "foods", "at", "that", "price"]} return this.lines .slice(1) // drop the first line, which contains desiredPrice .reduce( (accum, el: string): Map<number, Set<string>> => { // parseOneLine returns a single-entry Map. {price: ["food", "food"]}. let newEntry = this.parseOneLine(el); // mergeWith merges two maps. If there is a collision between keys, it // calls the passed-in function, and sets the value at that key // as the value returned from the lambda. Here, if the keys collide, we // know we must concat the food name to the list of food names already // at that price. return accum.mergeWith((oldVal, newVal) => { return oldVal.concat(newVal); }, newEntry); }, Map()) as Map<number, Set<string>>; }