JavaScript: better/cleaner functional way to find item from a nested array - javascript

I am trying to write a find function to find items from matched items from a potentially nested array (without having to flat the array first) and I am trying to write in a FP way.
Here is my attempt:
const nestedArray = [
[{ id: 1 }],
[{ id: 2 }],
[{ id: 3 }, [{ id: 4 }]],
{ id: 5 },
]
function findTarget(arr, predicate) {
const helper = ([x, ...xs]) =>
x === undefined
? null
: predicate(x)
? x
: Array.isArray(x)
? helper(x) ?? helper(xs)
: helper(xs)
return helper(arr)
}
findTarget(nestedArray, (item) => item.id === 5)
I think it works but it is not super readable and I am sure there are better ways to write such a function.

Here's how I would implement this using recursion:
function findTarget(value, predicate) {
const isArray = Array.isArray(value);
// Base case: if value is not array and predicate matches, we found a match
if (!isArray) {
if (predicate(value)) return value;
return null;
}
// value must be an array, so run recursion and see if value exists
for (const item of value) {
const foundItem = findTarget(item, predicate);
if (foundItem !== null) {
return foundItem;
}
}
// nothing found
return null;
}
does the same thing that your code does and imo looks cleaner.

Since your example is calling predicate(x) in the first place, it will return a false positive when matching an array with an id: 5 property, so the Array.isArray(x) should go first to avoid this:
const nestedArray = [
Object.assign([{ id: 1 }], { id: 5 }),
[{ id: 2 }],
[{ id: 3 }, [{ id: 4 }], null, [[{ id: 5 }]]],
{ id: 6 },
]
function findTargetLoop (arr, match) {
if (!Array.isArray(arr))
return arr && match(arr) ? arr : null;
let item, i = 0;
while (!(item = findTargetLoop(arr[i++], match)) && i < arr.length);
return item ?? null;
}
const findTargetFunc = (arr, match, next) =>
(next = ([item, ...rest]) =>
Array.isArray(item) ? next(item) ?? next(rest)
: item && match(item) ? item
: rest.length ? next(rest) : null)(arr);
const match = item => item.id === 5;
console.log('with iterations', findTargetLoop(nestedArray, match));
console.log('pure functional', findTargetFunc(nestedArray, match));

Here's one approach I can think of. It uses the init function as a sentinel value to distinguish whether the element being searched for has already been found. Before returning, it invokes the accumulated value which is either () => undefined, or () => curr capturing the first element that matches the predicate.
const flatFind = (array, predicate) => {
const init = () => undefined
const reducer = (prev, curr) => (
prev === init
? Array.isArray(curr)
? curr.reduce(reducer, init)
: predicate(curr)
? () => curr
: init
: prev
)
return array.reduce(reducer, init)()
}
const nestedArray = [
[{ id: 1 }],
[{ id: 2 }],
[{ id: 3 }, [{ id: 4 }]],
{ id: 5 },
]
console.log(flatFind(nestedArray, item => item.id === 5))

Your form is a good start but the helper is unnecessary and the order of conditions should be changed. Here we use inductive reasoning -
If x is undefined, there is nothing left to match. Return no match.
(inductive) x is defined. If x is an array, find within x or find within xs
(inductive) x is defined and x is a non-array. If x matches predicate f, return the match
(inductive) x is defined and x is a non-array that does not match the predicate f. Find within the sub-problem, xs.
const find = ([x, ...xs], f) =>
x === undefined // 1
? null
: Array.isArray(x) // 2
? find(x, f) ?? find(xs, f)
: f(x) // 3
? x
: find(xs, f) // 4
const t = [
[{ id: 1 }],
[{ id: 2 }],
[{ id: 3 }, [{ id: 4 }]],
{ id: 5 },
]
console.log(find(t, (item) => item?.id === 5)) // { id: 5 }
console.log(find(t, (item) => item?.id === 9)) // null
Note, the behavior of your findTarget checks child arrays against the predicate and allows for return of arrays that match the predicate. This is inconsistent behaviour as it's not possible to match the outermost array and the predicate checks for .id properties on arrays. The find solution above avoids this by changing the order of conditions 2 and 3. If you want the ability to return array matches with your function, you can change the order to 1,3,2,4.

Related

JS Decrease property after specific value

I have an array of objects, objects that contain an order: number; property.
I want for each object in that array that has the order property higher than a specific value to have it decreased it by one.
Any simple way to achieve this?
myArray.forEach(x => x.order >= someValue)...
You can map the result, updating the value when >= someValue
const someValue = 3;
const result = [
{ order: 1 },
{ order: 2 },
{ order: 3 },
{ order: 4 }
].map(x => x.order >= someValue ? { ...x, order: x.order - 1 } : x);
console.log(result);
With array.reduce
const value = 2;
const testArray = [{ order: 1 }, { order: 1 }, { order: 3 }];
const result = testArray.reduce((acc, curr) => {
if (curr.order > value) {
return [...acc, { ...curr, order: curr.order - 1 }];
}
return [...acc, curr];
}, []);
console.log(result);
Several options to reach this:
Adding functionality to Array.prototype.
if(!Array.prototype.decreaseOrderIfBiggerThan) {
Array.prototype.decreaseOrderIfBiggerThan = function(value) {
if(!value || typeof value != 'number') return this;
return this.map((el) => ({
...el,
order: el?.order > value ? --el.order : el.order
}))
}
}
Simply mapping:
myArray = myArray.map((el) => ({
...el,
order: el?.order > value ? --el.order : el.order
}))
Mutating original array: it's basically #2 method but without assigning new array to original one and with forEach.
Hopes this is what you needed.

Get combination of all elements in JavaScript Object

I'm trying to write a program that generates every single item based on my JSON structure and giving each combination an individual number. I found a function on here that so far does its job listing out every combination for me, however I cannot decipher the code to the point I actually even understand what it does. It gives me all the items, listen like Key : Value but honestly I have no idea what part of the code does what and I cannot access it in order to build in my giving them unique numbers. This is the code that I found on here (lost the thread to it):
function getCartesian(object) {
return Object.entries(object).reduce((r, [k, v]) => {
var temp = [];
r.forEach(s =>
(Array.isArray(v) ? v : [v]).forEach(w =>
(w && typeof w === 'object' ? getCartesian(w) : [w]).forEach(x =>
temp.push(Object.assign({}, s, { [k]: x }))
)
)
);
return temp;
}, [{}]);
}
var input = { bookSet: { book: ["book1", "book2", "book3"], title: ["title1", "title2"], author: ["author1"], publisher: ["publisher1"] } },
cartesian = { eachBook: getCartesian(input.bookSet) };
It's just written in a too advanced syntax for me to remotely understand where I have to insert myself to make any calculations. I guess what I'm asking for would be either an explanation or a somewhat more understandable and modifyable code. I definitely need to run through all elements like this is doing and the output looks great from what I could tell so far, I just need to somehow calculate the keys and have an output of a number derived of each object's elements' combined keys.
An example would be book 243 for title 2, author 4 and publisher 3. I hope anyone can make sense of this. Thanks a lot!
EDIT: Included my own data and desired output. The combinations I displayed don't need to make sense.
var Product = {
json: { Product : {
assortment: [
{
name: "Yoghurt",
Flavor: ["natural", "honey", "stracciatella"],
Kind: ["greek", "soy"],
},
{
name: "Sauce",
},
{
name: "Milk Drink",
}
],
Brand: ["Oatly", "Dannon"],
Containment: ["Cup", "Jar"]
}}};
My output I'd like to generate the combinations of all of those and ultimately calculate the numbers on the right in the following screenshot
Given C_1 and C_2 two sets
The cartesian product of C_1 and C_2
is given by C_1 x C_2 = {(c_1_i,c_2_j) for c_1_i in C_1, c_2_j in C_2}
You can build C_1 x C_2 x C_3 by considering (C_1 x C_2) (that you calculated before) and "adjoining" each elem of C_3 to a tuple of C_1 x C_2
And so forth
const cartesianProduct = (C, D) => {
const res = []
C.forEach(c => {
D.forEach(d => {
// in case the tuple contains only one element (the initialization)
// make the elmeent into a tuple
const tuple = Array.isArray(c) ? c : [c]
res.push([...tuple,d])
})
})
return res
}
const nCartesianProduct = (Cs_n) => {
// we adjoin each elem of C_i and we "grow"
return Cs_n.reduce((res, C_i) => cartesianProduct(res, C_i))
}
console.log(nCartesianProduct([['b1', 'b2', 'b3'], ['t1', 't2'], ['a'], ['p']]))
Here is my attempt to lay in a simple terms:
Lets assume an example of
const sets = [ [1], [1,2], [1,2,3] ]
Possible combinations may be logged as following:
1 1 1 1 2 1
1 1 2 -> 1 2 2
1 1 3 1 2 3
Lets think of it as a clock, where last row will increase the value of previous row, once it reaches its maximum. In another words: lets increase i position of the last row and when over the limit -> drop it to zero and increase sibling instead, where if sibling is over the top -> repeat.
Consider the following code:
let sets = [[1,2], [1,2,3], [1,2,3,4], [1,2,3,4,5] ];
let state = sets.map( () => 0 );
console.log(sets, state);
function handleIncreament(i){
if( state[i] >= sets[i].length){
if(i-1 < 0) {
console.log('end of the row');
return false;
}
state[i] = 0;
state[i-1] += 1;
return handleIncreament(i-1);
}
else {
return true;
}
}
while( handleIncreament(state.length - 1) ){
console.log( state );
state[state.length - 1]++;
}
Above will log as follows:
(4) [Array(2), Array(3), Array(4), Array(5)] (4) [0, 0, 0, 0]
(4) [0, 0, 0, 0]
(4) [0, 0, 0, 1]
(4) [0, 0, 0, 2]
(4) [0, 0, 0, 3]
(4) [0, 0, 0, 4]
(4) [0, 0, 1, 0]
(4) [0, 0, 1, 1]
(4) [0, 0, 1, 2]
...
(4) [1, 2, 3, 4]
end of the row
4
With that lets apply it to your example:
const test = { bookSet: { book: ["book1", "book2", "book3"], title: ["title1", "title2"], author: ["author1"], publisher: ["publisher1"] } };
sets = Object.values(test.bookSet);
state = sets.map( () => 0 );
console.log(sets, state);
const matches = [];
while( handleIncreament(state.length - 1) ){
const match = sets[0][state[0]] + ' ' + sets[1][state[1]] + ' ' + sets[2][state[2]] + ' ' + sets[3][state[3]];
matches.push( match );
state[state.length - 1]++
}
console.log(matches);
And expect to get the following:
["book1 title1 author1 publisher1", "book1 title2 author1 publisher1", "book2 title1 author1 publisher1", "book2 title2 author1 publisher1", "book3 title1 author1 publisher1", "book3 title2 author1 publisher1"]
You could take the above data without superfluous parts and simplify the result by creating a flat array of the nested properties.
The numbers of the result picture are not incuded, because of the missing relation of each value to the given data set.
function getCartesian(object) {
return Object.entries(object).reduce((r, [k, v]) => {
var temp = [];
r.forEach(s =>
(Array.isArray(v) ? v : [v]).forEach(w =>
(w && typeof w === 'object' ? getCartesian(w) : [w]).forEach(x =>
temp.push(Object.assign({}, s, { [k]: x }))
)
)
);
return temp;
}, [{}]);
}
var data = {
assortment: [
{
name: "Yoghurt",
Flavor: ["natural", "honey", "stracciatella"],
Kind: ["greek", "soy"],
},
{
name: "Sauce",
},
{
name: "Milk Drink",
}
],
Brand: ["Oatly", "Dannon"],
Containment: ["Cup", "Jar"]
},
result = getCartesian(data)
.map(({ assortment: { name, Flavor = '', Kind = '' }, d = '', Brand, f = '', Containment, h = '', i = '', j = '' }) =>
[name, Flavor, Kind, d, Brand, f, Containment, h, i, j]);
console.log(result.length);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Recursive function returns undefined regardless of enough return statements

I have read a few questions and answers on it already. It looks like my recursive function has got enough "return" statements, so... I do not know why it returns undefined... I have added extra log statement to show that the function itself finds the element, but does not return it...
let animals = [
{
name: "dogs",
id: 1,
children: [
{
name: "lessie",
id: 2
},
{
name: "bark-a-lot",
id: 3
}
]
},
{
name: "cats",
id: 4,
children: [
{
name: "meows-a-lot",
id: 5,
children: [
{
name: "meows-a-lot-in-the-morning",
id: 6
}
]
},
{
name: "whisk-ass",
id: 7
}
]
}
];
function recurseFind(node, id) {
if (Array.isArray(node)) {
return node.forEach(el => {
return recurseFind(el, id);
});
} else {
if (node.id === id) {
console.log("node matched", node.id, id, node);
return node;
} else if (node.children) {
return node.children.forEach(child => {
return recurseFind(child, id);
});
} else {
return "not found";
}
}
}
const found = recurseFind(animals, 6);
console.log("found", found, "wtf");
forEach returns undefined, so
return node.forEach(el => {
return recurseFind(el, id);
});
will always return undefined, no matter what the recursive calls find.
I'd use a for loop instead, and if a match is found, return it:
let animals = [
{
name: "dogs",
id: 1,
children: [
{
name: "lessie",
id: 2
},
{
name: "bark-a-lot",
id: 3
}
]
},
{
name: "cats",
id: 4,
children: [
{
name: "meows-a-lot",
id: 5,
children: [
{
name: "meows-a-lot-in-the-morning",
id: 6
}
]
},
{
name: "whisk-ass",
id: 7
}
]
}
];
function recurseFind(node, id) {
if (Array.isArray(node)) {
for (const el of node) {
const result = recurseFind(el, id);
if (result) return result;
}
} else {
if (node.id === id) {
return node;
} else if (node.children) {
for (const child of node.children) {
const result = recurseFind(child, id);
if (result) return result;
}
}
}
}
const found = recurseFind(animals, 6) || 'not found';
console.log("found", found);
VLAZ and CertainPerformance already pointed out why your function wasn't working.
Here's an alternative technique that seems a little simpler to me:
const recursiveFind = (pred) => (xs) => xs .reduce (
(r, x) => r != null ? r : pred (x) ? x : recursiveFind (pred) (x.children || []) || null,
null
)
const findById = (id) => recursiveFind(x => x.id == id)
const animals = [{name: "dogs", id: 1, children: [{name: "lessie", id: 2}, {name: "bark-a-lot", id: 3}]}, {name: "cats", id: 4, children: [{name: "meows-a-lot", id: 5, children: [{ name: "meows-a-lot-in-the-morning", id: 6}]}, {name: "whisk-ass", id: 7}]}];
console .log (findById (3) (animals))
console .log (findById (4) (animals))
We start with a generic function that searches for objects nested this way by whether they match the supplied predicate function. Then we pass it the predicate x => x.id == id to create a function that takes an id and then a list of values and finds the first value with matching ids in the list, or null if none are found.
If you have absolutely no use for this recursiveFind function, you can inline it into findById like this:
const findById = (id, xs) => xs .reduce (
(r, x) => r != null ? r : x.id == id ? x : findById (id, x.children || []) || null,
null
)
findById (3, animals)
But I actually would prefer to go in the other direction, and make it still more generic, using something like this:
const recursiveFind = (pred, descend) => (xs) => xs .reduce (
(r, x) => r != null ? r : pred (x) ? x : recursiveFind (pred, descend) (descend (x) || []) || null,
null
)
const findById = (id) => recursiveFind (x => x.id == id, x => x.children)
findById (3) (animals)
This version also parameterizes how we descend into the children of a node. In this case, we simply use x => x.children, but it's easy to imagine using other properties or a more complex method.
In all of these, do note that the function processes all nodes of your nested array structure, even when we've already found a match. If we have, the first check (r != null) skips ahead quickly, but if performance is critical, you might prefer a solution with explicit short-circuiting loops such as the one from CertainPerformance.

not using find twice when retrieving deeply nested object in javascript

I am able to find the object in the javascript below but it is pretty horrible and I am doing the find twice.
I'd be interested in a better way and one that did not involve lodash which I am current not using.
const statuses = [{
items: [{
id: 1,
name: 'foo'
}, {
id: 5,
name: 'bar'
}]
}, {
items: [{
id: 1,
name: 'mook'
}, {
id: 2,
name: 'none'
}, {
id: 3,
name: 'soot'
}]
}]
const selected = statuses.find(status => {
const none = status.items.find(alert => {
return alert.name === 'none';
});
return !!none;
});
console.log(selected)
const a = selected.items.find(s => s.name === 'none');
console.log(a)
You could use a nested some and find like this. This way you can skip the the operation once a match is found.
const statuses=[{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}];
let found;
statuses.some(a => {
const s = a.items.find(i => i.name === "none");
if (s) {
found = s;
return true
} else {
return false
}
})
console.log(found)
You could do something like this using map, concat and find. This is a bit slower but looks neater.
const statuses=[{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}]
const found = [].concat(...statuses.map(a => a.items))
.find(a => a.name === "none")
console.log(found)
Here's a jsperf comparing it with your code. The first one is the fastest.
You could combine all the status items into one array with reduce() and then you only have to find() once:
const statuses = [{
items: [{
id: 1,
name: 'foo'
}, {
id: 5,
name: 'bar'
}]
}, {
items: [{
id: 1,
name: 'mook'
}, {
id: 2,
name: 'none'
}, {
id: 3,
name: 'soot'
}]
}]
const theNones = statuses
.reduce(function(s, t) { return s.items.concat(t.items); })
.find(function(i) { return i.name === 'none'; });
console.log(theNones);
You could use flatMap and find:
Note: Array.prototype.flatMap is in Stage 3 and not part of the language yet but ships in most environments today (including Babel).
var arr = [{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}]
console.log(
arr
.flatMap(({ items }) => items)
.find(({
name
}) => name === 'none')
)
We could polyfill the flatting with concating (via map + reduce):
var arr = [{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}];
console.log(
arr
.map(({ items }) => items)
.reduce((items1, items2) => items1.concat(items2))
.find(({
name
}) => name === 'none')
)
You could also reduce the result:
var arr = [{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}]
console.log(
arr.reduce((result, {
items
}) =>
result ? result : items.find(({
name
}) => name === 'none'), null)
)
One reduce function (one iteration over everything), that returns an array of any object matching the criteria:
const [firstOne, ...others] = statuses.reduce((found, group) => found.concat(group.items.filter(item => item.name === 'none')), [])
I used destructuring to mimic your find idea, as you seem chiefly interested in the first one you come across. Because this iterates only once over each item, it is better than the alternative answers in terms of performance, but if you are really concerned for performance, then a for loop is your best bet, as the return will short-circuit the function and give you your value:
const findFirstMatchByName = (name) => {
for (let group of statuses) {
for (let item of group.items) {
if (item.name === name) {
return item
}
}
}
}
findFirstMatchByName('none')

what are the ways to write an algorithm that add a property to each object of a nested array in javascript?

const data = [
1,2,3,4,5,[6,7,8],
]
function goEach(items) {
if (items.length) {
items.forEach((item) => goEach(item))
} else {
console.log(items);
}
}
let newList = [];
function buildNewList(items) {
if (items.length) {
items.forEach((item) => buildNewList(item))
} else {
newList.push(items);
// console.log(items);
}
}
let simList = [];
function buildSimList(items) {
if (items.length) {
items.forEach((item) => buildSimList(item))
} else {
simList.push({id: items, checked: false});
// console.log(items);
}
}
buildSimList is what I am working on.
I know how to traverse each item as well as flattening a nested array with recursion. However, my brain getting stucked on the best way to produce an array with identical array structure but in different data format. For example, my buildSimList should be converting the data array into an array similar to this
[
{id: 1, clicked: false},
{id: 2, clicked: false},
{id: 3, clicked: false},
{id: 4, clicked: false},
{id: 5, clicked: false},
[
{id: 6, clicked: flase},
{id: 7, clicked: flase},
{id: 8, clicked: flase},
],
]
Is there an intuitive way to achieve it? I can't think of any. even using a library like lodash can be a good solution as well.
You were close since you do one object for non arrays and recurse when not, but you are adding to the same array and thus that will result in a flattened structure. Here is the same where each array become an array and each element become an object using higher order functions:
const data = [1,2,3,4,5,[6,7,8]]
function convert(e) {
return e instanceof Array ?
e.map(convert) :
{ id: e, clicked: false };
}
console.log(convert(data));
Also know that using the function twice won't give you the same oddity that you get the earlier result combined as in your code. The reson for that is that I do not use a global accumulator. Should I use an accumulator I would do something like this:
function convert(e) {
function helper(e, acc) {
...
}
return helper(e, []);
}
or if you really want mutation:
function convert(e) {
let acc = [];
function helper(e) {
acc.push(...);
}
helper(e);
return acc;
}
Explanation uses symbols from my code snippet
When iterating data within convert we'll have to cover for 2 cases:
numOrArray is a number, in which case we map it toObject
numOrArray is an array, in which case we start a nested iteration. This may be a recursive call, since the logic is identical for nested arrays.
// Converts given number to an object with id and clicked properties:
const toObject = (num) => ({ id: num, clicked: false });
// Iterates given data and converts all numbers within to
// objects with toObject. If an array is encountered,
// convert is called recursively with that array as argument.
const convert = (data) => {
return data.map((numOrArray) => Array.isArray(numOrArray)
? convert(numOrArray)
: toObject(numOrArray));
}
// Test drive!
const data = [1, 2, 3, 4, 5, [6, 7, 8]];
console.log(convert(data));
Both converting items in a nested array, and flattening such an array are pretty simple if you use a recursive function.
All you have to do go through each item, and if it's not another array, perform the transformation you want. Otherwise, recurse. Here's an example for the data in your snippet:
const data = [1,2,3,4,5,[6,7,8]]
function convert(arr) {
return arr.map(item => {
if (item instanceof Array) return convert(item);
return {id: item, clicked: false};
});
}
const mapped = convert(data);
console.log(mapped);
function flatten(arr) {
return [].concat(...arr.map(item => {
if (item instanceof Array) return flatten(item);
return [item];
}));
}
const flat = flatten(mapped);
console.log(flat)
Use forEach function along with recursion and check for the element's type using either typeof keyword or instanceof keyword.
const data = [1, 2, 3, 4, 5, [6, 7, 8]];
let convert = (acc /*This is the current accumulator*/ , array) => {
array.forEach((n) => {
if (n instanceof Array) {
let newAcc = []; // New accumulator because the iteration found an Array.
acc.push(newAcc);
convert(newAcc, n); // Recursion for the new accumulator
} else acc.push({ id: n, clicked: false });
});
};
let result = [];
convert(result, data);
console.log(result);
.as-console-wrapper {
max-height: 100% !important
}
When you want to transform an array to another array, the first thing to think of is map. Given your requirements, here's my solution to your problem:
const data = [
1, 2, 3, 4, 5, [6, 7, 8]
];
const buildSimList = (array) =>
// transform array with map
array.map(item =>
// check if current item is array
item instanceof Array
// if it is an array then recursively transform it
? buildSimList(item)
// if not, then convert the number to an object
: { id: item, checked: false }
);
console.log(buildSimList(data));
I would recommend a generic function like Array.prototype.map but one the maps nested arrays too – we'll call it deepMap.
const identity = x =>
x
const deepMap = (f = identity, xs = []) =>
xs.map (x =>
Array.isArray (x)
? deepMap (f, x)
: f (x))
const makeObject = (id = 0, clicked = false) =>
({ id, clicked })
const data =
[ 1, 2, 3, 4, 5, [ 6, 7, 8 ] ]
console.log (deepMap (makeObject, data))
// [ { id : 1, clicked : false }
// , { id : 2, clicked : false }
// , { id : 3, clicked : false }
// , { id : 4, clicked : false }
// , { id : 5, clicked : false }
// , [ { id : 6, clicked : false }
// , { id : 7, clicked : false }
// , { id : 8, clicked : false }
// ]
// ]
We get a better picture of how this works if we first understand map, which only performs a shallow transformation – Below, we reimplement deepMap but this time using our own implementation of map
const identity = x =>
x
const None =
Symbol ()
const map = (f = identity, [ x = None, ...xs ] = []) =>
x === None
? []
: [ f (x) , ...map (f, xs) ]
const deepMap = (f = identity, xs = []) =>
map (x =>
Array.isArray (x)
? deepMap (f, x)
: f (x), xs)
const makeObject = (id = 0, clicked = false) =>
({ id, clicked })
const data =
[ 1, 2, 3, 4, 5, [ 6, 7, 8 ] ]
console.log (deepMap (makeObject, data))
// [ { id : 1, clicked : false }
// , { id : 2, clicked : false }
// , { id : 3, clicked : false }
// , { id : 4, clicked : false }
// , { id : 5, clicked : false }
// , [ { id : 6, clicked : false }
// , { id : 7, clicked : false }
// , { id : 8, clicked : false }
// ]
// ]

Categories