Skip to content

Instantly share code, notes, and snippets.

@antonkuzmin
Last active March 2, 2022 09:36
Show Gist options
  • Select an option

  • Save antonkuzmin/665cb3b96c1df9b3f5483be3528de185 to your computer and use it in GitHub Desktop.

Select an option

Save antonkuzmin/665cb3b96c1df9b3f5483be3528de185 to your computer and use it in GitHub Desktop.
Function, that traverse through arrays and objects. The behaviour and callback signature mimics Array.reduce().
const isArray = (x: any): x is Array<any> =>
Array.isArray(x);
const isObject = (x: any): x is Object =>
x !== null && typeof x === 'object' && !(x instanceof Date || x instanceof Number || x instanceof String);
type ReduceRecursiveCb<OUT, IN> = (acc: OUT, x, path: Array<string | number>, source: IN, parent) => OUT;
export const reduceRecursive = <OUT, IN = any>(fn: ReduceRecursiveCb<OUT, IN>) =>
(initialValue: OUT, input: any, path: Array<string | number> = [], source: IN = input, parent?: any): OUT => {
let out = input === undefined
? initialValue
: fn(initialValue, input, path, source, parent);
if (isArray(input)) {
out = input.reduce(
(acc, x, i) => reduceRecursive(fn)(acc, x, [...path, i], source, input),
out
);
} else if (isObject(input)) {
out = Object.entries(input).reduce(
(acc, [k, x]) => reduceRecursive(fn)(acc, x, [...path, k], source, input),
out
);
}
return out;
}
// Example:
const users = [
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "[email protected]",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets",
"geo": {
"lat": "-37.6102",
"lng": "-80.9653"
}
}
},
{
"id": 2,
"name": "Ervin Howell",
"username": "Antonette",
"email": "[email protected]",
"address": {
"street": "Victor Plains",
"suite": "Suite 879",
"city": "Wisokyburgh",
"zipcode": "90566-7771",
"geo": {
"lat": "-43.9509",
"lng": "-34.4618"
}
},
"phone": "010-692-6593 x09125",
"website": "anastasia.net",
"company": {
"name": "Deckow-Crist",
"catchPhrase": "Proactive didactic contingency",
"bs": "synergize scalable supply-chains",
"geo": {
"lat": "-43.6102",
"lng": "-34.0653"
}
}
},
{
"id": 3,
"name": "Clementine Bauch",
"username": "Samantha",
"email": "[email protected]",
"address": {
"street": "Douglas Extension",
"suite": "Suite 847",
"city": "McKenziehaven",
"zipcode": "59590-4157",
"geo": {
"lat": "-68.6102",
"lng": "-47.0653"
}
},
"phone": "1-463-123-4447",
"website": "ramiro.info",
"company": {
"name": "Romaguera-Jacobson",
"catchPhrase": "Face to face bifurcated interface",
"bs": "e-enable strategic applications",
"geo": {
"lat": "-68.3102",
"lng": "-47.2653"
}
}
}
];
const coords = reduceRecursive<[number, number][]>((acc, x) => {
if (x.geo)
acc.push([x.geo.lat, x.geo.lng]);
return acc;
})([], users);
console.log(coords);
// Output:
// [
// [ '-37.3159', '81.1496' ],
// [ '-37.6102', '-80.9653' ],
// [ '-43.9509', '-34.4618' ],
// [ '-43.6102', '-34.0653' ],
// [ '-68.6102', '-47.0653' ],
// [ '-68.3102', '-47.2653' ]
// ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment