I have this array:
const nums = [
{name: 'a', option: false},
{name: 'b', option: false},
{name: 'c', option: false},
{name: 'd', option: false},
{name: 'e', option: false},
{name: 'f', option: true},
{name: 'g', option: true},
{name: 'h', option: false},
{name: 'i', option: true},
{name: 'j', option: false},
{name: 'k', option: false},
];
I would like to reorganize this array so that elements so that when moving through the array, elements that have the "false" option are grouped together by groups of 4 elements, but if there is an element that has the "true" option, it takes precedence and is put before the groups of 4 elements, except if the preceding group of 4 elements preceding is complete (ie its length is equal to 4).
The result would be
const nums = [
{name: 'a', option: false},
{name: 'b', option: false},
{name: 'c', option: false},
{name: 'd', option: false},
{name: 'f', option: true},
{name: 'g', option: true},
{name: 'i', option: true},
{name: 'e', option: false},
{name: 'h', option: false},
{name: 'j', option: false},
{name: 'k', option: false},
];
I have succeeded in a imperative style
let stock = [];
let array2 = [];
for (let i = 0; i < nums.length; i ++){
if (nums[i].option){
array2.push(nums[i]);
}
else {
stock.push(nums[i]);
if(stock.length == 4) {
array2 = array2.concat(stock);
stock = [];
}
}
}
console.log('array2 is', array2);
So far, so good I would like now to do it in a functional manner This is my way:
let stock = [];
const actual = nums.map(
(acc => val => {
if (val.option){
acc.push(val);
}
else {
stock.push(val);
if (stock.length === 4) {
acc = acc.concat(stock);
stock = [];
}
}
return acc;
})([]))
console.log('actual is, ', actual[actual.length-1]);
While it works, I think that the functional programming style could be improved. Could you help me improve my code ?
Note: Some people have found the question a bit unusual (which it is indeed). So I will provide some more context:
the underlying problematic is a UI style problem that cannot be solved with conventional style classes: I have a grid container that contains lines of four grid elements (when they are not "opened").
When it is opened, then the card takes all the line and the following elements are push back. The problem is that it is not pretty, because I have some lines that may contain only one, or two non opened elements, and nothing after until the next line, the opened card that takes all the space of the next line.
So the idea is to take groups of four non opened elements, stock them until I have a group of four, and flush them on one line. If I meet an opened card, I display this card first, and flush the non opened elements after.
I'd actually go for an approach that is similar to yours,
using a chunk as a temporary value to store incomplete groups.
const group = (data = [], chunk = [], res = []) => {
if (!data.length) {
// input is empty,
// flush any possible incomplete chunk
// and return result
return [...res, ...chunk];
}
const [head, ...tail] = data;
const next = (...xs) => group(tail, ...xs);
if (head.option) {
// head.option is true, so you can
// safely put it in tail position
return next(chunk, [...res, head]);
}
if (chunk.length < 4) {
// chunk is not-complete, so keep accumulating
return next([...chunk, head], res);
}
// chunk is complete, append it to the rest
// and create a new one with the current value
return next([head], [...res, ...chunk]);
};
const data = [
{name: 'a', option: false},
{name: 'b', option: false},
{name: 'c', option: false},
{name: 'd', option: false},
{name: 'e', option: false},
{name: 'f', option: true},
{name: 'g', option: true},
{name: 'h', option: false},
{name: 'i', option: true},
{name: 'j', option: false},
{name: 'k', option: false},
];
console.log('result is', group(data));
Recursion is desirable here and their states are:
chunk, (res + head)(chunk + head), reshead, (res + chunk)If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With