Let’s put our newfound knowledge around Generics to the test by building out some higher-order functions that operate on dictionaries:
tsTry
///// SAMPLE DATA FOR YOUR EXPERIMENTATION PLEASURE (do not modify)constfruits = {apple : {color : "red",mass : 100 },grape : {color : "red",mass : 5 },banana : {color : "yellow",mass : 183 },lemon : {color : "yellow",mass : 80 },pear : {color : "green",mass : 178 },orange : {color : "orange",mass : 262 },raspberry : {color : "red",mass : 4 },cherry : {color : "red",mass : 5 },}interfaceDict <T > {[k : string]:T }// Array.prototype.map, but for DictfunctionmapDict (...args : any[]): any {}// Array.prototype.filter, but for DictfunctionfilterDict (...args : any[]): any {}// Array.prototype.reduce, but for DictfunctionreduceDict (...args : any[]): any {}////////////////////////////////////////////////////// TEST SUITE /////////////////////// no need to modify these /////////////////////////////////////////////////// MAPconstParameter 'fruit' implicitly has an 'any' type.Parameter 'name' implicitly has an 'any' type.7006fruitsWithKgMass =mapDict (fruits , (, fruit ) => ({ name
7006Parameter 'fruit' implicitly has an 'any' type.Parameter 'name' implicitly has an 'any' type....fruit ,kg : 0.001 *fruit .mass ,name ,}))constlemonName : string =fruitsWithKgMass .lemon .name // @ts-ignore-errorconstfailLemonName : number =fruitsWithKgMass .lemon .name assertOk (fruitsWithKgMass ,"[MAP] mapDict returns something truthy")assertEquals (fruitsWithKgMass .cherry .name ,"cherry",'[MAP] .cherry has a "name" property with value "cherry"')assertEquals (fruitsWithKgMass .cherry .kg ,0.005,'[MAP] .cherry has a "kg" property with value 0.005')assertEquals (fruitsWithKgMass .cherry .mass ,5,'[MAP] .cherry has a "mass" property with value 5')assertEquals (Object .keys (fruitsWithKgMass ).length ,8,"[MAP] fruitsWithKgMass should have 8 keys")// FILTER// only red fruitsconstredFruits =filterDict (fruits ,(Parameter 'fruit' implicitly has an 'any' type.7006Parameter 'fruit' implicitly has an 'any' type.) => fruit fruit .color === "red")assertOk (redFruits ,"[FILTER] filterDict returns something truthy")assertEquals (Object .keys (redFruits ).length ,4,"[FILTER] 4 fruits that satisfy the filter")assertEquals (Object .keys (redFruits ).sort ().join (", "),"apple, cherry, grape, raspberry",'[FILTER] Keys are "apple, cherry, grape, raspberry"')// REDUCE// If we had one of each fruit, how much would the total mass be?constoneOfEachFruitMass =reduceDict (fruits ,(Parameter 'currentMass' implicitly has an 'any' type.Parameter 'fruit' implicitly has an 'any' type.7006, currentMass ) => fruit currentMass +fruit .mass ,
7006Parameter 'currentMass' implicitly has an 'any' type.Parameter 'fruit' implicitly has an 'any' type.0)assertOk (redFruits ,"[REDUCE] reduceDict returns something truthy")assertEquals (typeofoneOfEachFruitMass ,"number","[REDUCE] reduceDict returns a number")assertEquals (oneOfEachFruitMass ,817,"[REDUCE] 817g mass if we had one of each fruit")
Spoiler warning // Click to reveal answer
tsTry
///// SAMPLE DATA FOR YOUR EXPERIMENTATION PLEASURE (do not modify)constfruits = {apple : {color : "red",mass : 100 },grape : {color : "red",mass : 5 },banana : {color : "yellow",mass : 183 },lemon : {color : "yellow",mass : 80 },pear : {color : "green",mass : 178 },orange : {color : "orange",mass : 262 },raspberry : {color : "red",mass : 4 },cherry : {color : "red",mass : 5 },}interfaceDict <T > {[k : string]:T }// Array.prototype.map, but for DictfunctionmapDict <T ,S >(inputDict :Dict <T >,mapFunction : (original :T ,key : string) =>S ):Dict <S > {constoutDict :Dict <S > = {}for (letk ofObject .keys (inputDict )) {constthisVal =inputDict [k ]outDict [k ] =mapFunction (thisVal ,k )}returnoutDict }// Array.prototype.filter, but for DictfunctionfilterDict <T >(inputDict :Dict <T >,filterFunction : (value :T ,key : string) => boolean):Dict <T > {constoutDict :Dict <T > = {}for (letk ofObject .keys (inputDict )) {constthisVal =inputDict [k ]if (filterFunction (thisVal ,k ))outDict [k ] =thisVal }returnoutDict }// Array.prototype.reduce, but for DictfunctionreduceDict <T ,S >(inputDict :Dict <T >,reducerFunction : (currentVal :S ,dictItem :T ,key : string) =>S ,initialValue :S ):S {letvalue =initialValue for (letk ofObject .keys (inputDict )) {constthisVal =inputDict [k ]value =reducerFunction (value ,thisVal ,k )}returnvalue }////////////////////////////////////////////////////// TEST SUITE /////////////////////// no need to modify these /////////////////////////////////////////////////// MAPconstfruitsWithKgMass =mapDict (fruits , (fruit ,name ) => ({...fruit ,kg : 0.001 *fruit .mass ,name ,}))constlemonName : string =fruitsWithKgMass .lemon .name // @ts-ignore-errorconstfailLemonName : number =fruitsWithKgMass .lemon .name assertOk (fruitsWithKgMass ,"[MAP] mapDict returns something truthy")assertEquals (fruitsWithKgMass .cherry .name ,"cherry",'[MAP] .cherry has a "name" property with value "cherry"')assertEquals (fruitsWithKgMass .cherry .kg ,0.005,'[MAP] .cherry has a "kg" property with value 0.005')assertEquals (fruitsWithKgMass .cherry .mass ,5,'[MAP] .cherry has a "mass" property with value 5')assertEquals (Object .keys (fruitsWithKgMass ).length ,8,"[MAP] fruitsWithKgMass should have 8 keys")// FILTER// only red fruitsconstredFruits =filterDict (fruits ,(fruit ) =>fruit .color === "red")assertOk (redFruits ,"[FILTER] filterDict returns something truthy")assertEquals (Object .keys (redFruits ).length ,4,"[FILTER] 4 fruits that satisfy the filter")assertEquals (Object .keys (redFruits ).sort ().join (", "),"apple, cherry, grape, raspberry",'[FILTER] Keys are "apple, cherry, grape, raspberry"')// REDUCE// If we had one of each fruit, how much would the total mass be?constoneOfEachFruitMass =reduceDict (fruits ,(currentMass ,fruit ) =>currentMass +fruit .mass ,0)assertOk (redFruits ,"[REDUCE] reduceDict returns something truthy")assertEquals (typeofoneOfEachFruitMass ,"number","[REDUCE] reduceDict returns a number")assertEquals (oneOfEachFruitMass ,817,"[REDUCE] 817g mass if we had one of each fruit")