In the below data structure,
data = {
data: [{
areas: [{
sections: [{
rjf: [{
type: 'heading-1'
text: 'Sample Heading',
}]
},
{
rjf: [{
type: 'paragraph',
depth: 0,
text: 'This website is offered to you by:',
inlineStyleRanges: [],
inlineEntityRanges: []
},
{
"type": "ordered-list-item",
"text": "Ordered Item A",
},
{
"type": "unordered-list-item",
"text": "Ordered Item B",
},
{
type: 'paragraph',
depth: 0,
text: 'This website is offered to you by:',
inlineStyleRanges: [],
inlineEntityRanges: []
},
{
"type": "ordered-list-item",
"text": "Ordered Item A",
},
{
"type": "unordered-list-item",
"text": "Ordered Item B",
}
]
}
]
}]
}]
};
I'm trying to group all the type which as ordered-list-item & unordered-list-item into new object. Something like below,
{
"type": 'list',
"items": [
{
"type": "ordered-list-item",
"text": "Ordered Item A",
},
{
"type": "unordered-list-item",
"text": "Ordered Item B",
}
]
}
After so much treble, I came with the below solution. This works fine.
But having an issue, In rjf if ordered-list-item & unordered-list-item not found nothing should be happen unfortunately and empty list getting added to rjf.
Below is the code snippet, please help me to fix this issue.
const data = {
data: [{
areas: [{
sections: [{
rjf: [{
text: 'Sample Heading',
}]
},
{
rjf: [{
type: 'paragraph',
depth: 0,
text: 'This website is offered to you by:',
inlineStyleRanges: [],
inlineEntityRanges: []
},
{
"type": "ordered-list-item",
"text": "Ordered Item A",
},
{
"type": "unordered-list-item",
"text": "Ordered Item B",
},
{
type: 'paragraph',
depth: 0,
text: 'This website is offered to you by:',
inlineStyleRanges: [],
inlineEntityRanges: []
},
{
"type": "ordered-list-item",
"text": "Ordered Item A",
},
{
"type": "unordered-list-item",
"text": "Ordered Item B",
}
]
}
]
}]
}]
};
const moveToNewObject = (data) => {
const sections = data[0].areas[0].sections;
sections.forEach(data => {
let list = data.rjf;
let a = list.map((entry, index) => {
return { ...entry,
index,
use: entry.type !== 'unordered-list-item' && entry.type !== 'ordered-list-item'
}
}).filter(entry => entry.use).map((entry, index, entries) => {
const end = index < entries.length - 1 ? entries[index + 1].index : list.length - entry.index;
return [{
type: entry.type,
text: entry.text
}, {
type: 'list',
items: list.slice(entry.index + 1, entry.index + end)
}]
});
console.log(a);
});
}
console.log(moveToNewObject(data.data));
2 Answers 2
Your data has a very weird structure which makes it harder to be honest. The snippet below uses a function that uses map on all your sections, and if rjf type is 'unordered-list-item' or 'ordered-list-item', it moves it to a new rjf of type list as items. Hopefully it's what you wanted.
And here's a fiddle if you want clearer code formatting: https://jsfiddle.net/qce2vLr3/
const data = {
data: [
{
areas: [
{
sections: [
{
rjf: [
{
text: 'Sample Heading',
}
]
},
{
rjf: [
{
type: 'paragraph',
depth: 0,
text: 'This website is offered to you by:',
inlineStyleRanges: [],
inlineEntityRanges: []
},
{
"type": "ordered-list-item",
"text": "Ordered Item A",
},
{
"type": "unordered-list-item",
"text": "Ordered Item B",
}
]
}
]
}
]
}
]
};
const moveToNewObject = (data) => {
const sections = data[0].areas[0].sections; // why is data an array?
return sections.map((section) => {
if (section.rjf) {
const looseItems = section.rjf.filter((rjf) => rjf.type && ['ordered-list-item', 'unordered-list-item'].includes(rjf.type));
if (looseItems.length) {
return {
rjf: [
...section.rjf,
{
type: 'list',
items: looseItems
}
].filter((rjf) => rjf.type && !['ordered-list-item', 'unordered-list-item'].includes(rjf.type))
}
}
return section;
}
return section;
})
}
data.data[0].areas[0].sections = moveToNewObject(data.data);
console.log(data.data);
UPDATE
Here's a solution that "groups" your lists by multiple headings: https://jsfiddle.net/pkLyd0gh/
const data = {
data: [
{
areas: [
{
sections: [
{
rjf: [
{
text: 'Sample Heading',
}
]
},
{
rjf: [
{
"type": "heading-1",
"text": "A title",
},
{
"type": "ordered-list-item",
"text": "Ordered Item A",
},
{
"type": "unordered-list-item",
"text": "Ordered Item B",
},
{
"type": "heading-2",
"text": "A title",
},
{
"type": "ordered-list-item",
"text": "Ordered Item C",
},
{
"type": "unordered-list-item",
"text": "Ordered Item D",
}
]
}
]
}
]
}
]
};
const reformattedSections = (data) => {
const sections = data[0].areas[0].sections;
const listItemTypes = ['unordered-list-item', 'ordered-list-item'];
return sections.map((section) => {
let lastHeadingIndex = -1;
return section.rjf.reduce((acc, current, index) => {
if (!current.type || !listItemTypes.includes(current.type)) {
lastHeadingIndex = acc.length;
return [...acc, current]
}
else {
let listObject = acc.find((el, i) => i > lastHeadingIndex && i < index && el.type === 'list');
if (!listObject) {
listObject = {
type: 'list',
items: [current]
}
return [...acc, listObject];
}
listObject.items = [...listObject.items, current];
return acc;
}
}, [])
})
}
data.data[0].areas[0].sections = reformattedSections(data.data);
console.log('sections', data.data);
9 Comments
ordered-list-item & unordered-list-item is not maintained. For example, after the list if I get a paragraph grouping should stop.ordered-list-item & unordered-list-item into type list. Your new example shows that the results are doubled?heading type being followed by it's contents (i.e. type list)? It is probably doable (use reduce instead of map plus array destructuring) but I would advice you against doing that. Introducing such logic makes code hard to maintain and unreadable. Array is not a proper data structure for this kind of thing, you'd better use an objects such as { heading: <your heading object>, contents: <i.e. your list> for this. If you get this input from your Backend, it's backend's job to provide it in readable format.slice is a method on array's not on objects. DATA in your code is an Object
const a ={a:1,b:2,c:3,d:4}
const b =[1,2,3,4]
//console.log(a.slice(1,3)) //this will return the error
console.log(b.slice(1,3))
Hope it helps.