ageName:=T(27,"Jonah", 18,"Alan", 28,"Glory", 18,"Popeye", 28,"Alan"); nameNemesis:=T("Jonah","Whales", "Jonah","Spiders", "Alan","Ghosts", "Alan","Zombies", "Glory","Buffy"); fcn addAN(age,name,d){ // keys are names, values are ( (age,...),() ) if (r:=d.find(name)) d[name] = T(r[0].append(age),r[1]); else d.add(name,T(T(age),T)); d // return d so pump will use that as result for assignment } fcn addNN(name,nemesis,d){ // values-->( (age,age...), (nemesis,...) ) if (r:=d.find(name)){ ages,nemesises := r; d[name] = T(ages,nemesises.append(nemesis)); } } // Void.Read --> take existing i, read next one, pass both to next function var d=ageName.pump(Void,Void.Read,T(addAN,Dictionary())); nameNemesis.pump(Void,Void.Read,T(addNN,d)); d.println(); // the union of the two tables d.keys.sort().pump(Console.println,'wrap(name){ //pretty print the join val:=d[name]; if (not val[1])return(Void.Skip); String(name,":",d[name][1].concat(",")); })