I am trying to split up an array of components into individual arrays depending on if there is a list breaking the pattern of heading, paragraph or hr. Here is what i have tried:
const components = [
{ name: 'heading' },
{ name: 'paragraph' },
{ name: 'hr' },
{ name: 'heading' },
{ name: 'list' },
{ name: 'hr' },
{ name: 'paragraph' },
{ name: 'list' }
];
const richText = [];
const list = [];
components.forEach((component, index) => {
switch (component.name) {
case 'heading': case 'hr': case 'paragraph':
richText.push(component.name)
break
case 'list':
list.push(`${component.name}-index`)
break
}
})
console.log(richText)
Current output is:
[
{ name: 'heading' },
{ name: 'paragraph' },
{ name: 'hr' },
{ name: 'heading' },
{ name: ‘hr’ },
{ name: 'paragraph' }
]
Desired output is:
[
{ name: 'heading' },
{ name: 'paragraph' },
{ name: 'hr' },
{ name: 'heading' }
],
[
{ name: ‘hr’ },
{ name: 'paragraph' }
]
Considering list as a break, you can make initialization for richText as [[]]. You can keep pushing to last index of richText until a list is found. If a list is found push an empty array [] to your richText array.
Working Fiddle
const components = [
{ name: 'heading' },
{ name: 'paragraph' },
{ name: 'hr' },
{ name: 'heading' },
{ name: 'list' },
{ name: 'hr' },
{ name: 'paragraph' },
{ name: 'list' }
];
const richText = [[]];
const list = [];
components.forEach((component, index) => {
switch (component.name) {
case 'heading': case 'hr': case 'paragraph':
richText[richText.length - 1].push(component);
break
case 'list':
list.push({name: `${component.name}-${richText.length}`})
index < components.length - 1 ? richText.push([]) : {};
break
}
})
console.log(richText);
console.log(list);
Same logic Array.reduce implementation
const components = [
{ name: 'heading' },
{ name: 'paragraph' },
{ name: 'hr' },
{ name: 'heading' },
{ name: 'list' },
{ name: 'hr' },
{ name: 'paragraph' },
{ name: 'list' }
];
const richText = components.reduce((acc, curr, index) => {
if(curr.name === 'list' && index < components.length - 1) {
acc.push([]);
} else {
acc[acc.length - 1].push(curr);
}
return acc;
}, [[]]);
console.log(richText);
How about
const components = [
{ name: 'heading' },
{ name: 'paragraph' },
{ name: 'hr' },
{ name: 'heading' },
{ name: 'list' },
{ name: 'hr' },
{ name: 'paragraph' },
{ name: 'list' }
];
let richText = [];
let list = [];
components.forEach((component, index) => {
switch (component.name) {
case 'heading': case 'hr': case 'paragraph':
richText.push({'name': component.name})
break
case 'list':
list.push(richText);
richText = [];
break
}
})
console.log(list);
Here's a generic function akin to python's groupby. Given an iterable and a key function, it yields pairs [key, chunk] where each chunk contains sequential items that have the same key:
function* groupBy(iter, keyFn) {
let last = null, buf = []
for (let item of iter) {
let key = keyFn(item)
if (key !== last) {
if (buf.length)
yield [last, buf]
buf = []
}
buf.push(item)
last = key
}
yield [last, buf]
}
Applied to your problem:
for (let [isList, tags] of groupBy(components, c => c.name === 'list'))
if (isList) ... else ...