I'm trying to split an array of articles into subarrays based on the first key value pair, and it's order.
I have looked into many Stack Overflow posts, and I think this is the one that closest suits what I'm trying to accomplish:
break array of objects into separate arrays based on a property
I am aware that questions around reduce are commonly asked, but I'm getting tripped up and this is what I can't find the answer to:
What's different: I don't want to filter the arrays into 2 separate categories (ie. "markup" and "video"), but rather have the first array be all of the "markup" items UNTIL a "video" item, make an array with all of the "video" items until next "markup" item, make a new array of all of the "markup" items until next "video" item, etc.
Here is a REPL reproducing what I'm trying to do: REPL reproducing problem
The data looks like this:
export default [{
"type": "markup",
"element": "p",
"html": "blah"
}, {
"type": "markup",
"element": "p",
"html": "blah"
}, {
"type": "markup",
"element": "p",
"html": "blah"
}, {
"type": "embeddedVideo",
"element": "p",
"html": "embeddedWidget"
}, {
"type": "markup",
"element": "p",
"html": "blah"
},
{
"type": "markup",
"element": "p",
"html": "blah"
},
]
What I would like it to look like after using JavaScript reduce is:
[
[
{type: 'markup', element: 'p', html: 'blah' /*...*/ },
{type: 'markup', element: 'p', html: 'blah' /*...*/ },
{type: 'markup', element: 'p', html: 'blah' /*...*/ }
],
[
{type: 'embeddedVideo', /*...*/ }
],
[
{type: 'markup', element: 'p', html: 'blah' /*...*/ },
{type: 'markup', element: 'p', html: 'blah' /*...*/ },
{type: 'markup', element: 'p', html: 'blah' /*...*/ }
]
]
What I have so far is:
import articleBody from './articleBody.js';
function groupBy(arr, property) {
return arr.reduce((prop, x) => {
if (!prop[x[property]]) { prop[x[property]] = []; }
prop[x[property]].push(x);
return prop;
}, {});
}
let outputSample = groupBy(articleBody, "type");
console.log(outputSample)
This code simply created 2 arrays (one with markup, one with video), it doesn't keep in mind the order of the original data, nor does it create separate arrays of all the groups of data based on order.
What is the most elegant way to solve this problem? I would be very appreciative if you could even guide me in the right direction.
You could use Array#reduce and check the last element of the accumulator.
var array = [{ type: "markup", element: "p", html: "blah" }, { type: "markup", element: "p", html: "blah" }, { type: "markup", element: "p", html: "blah" }, { type: "embeddedVideo", element: "p", html: "embeddedWidget" }, { type: "markup", element: "p", html: "blah" }],
grouped = array.reduce((r, o, i, a) => {
var last = r[r.length - 1];
if (!last || last[0].type !== o.type) {
r.push([o]);
} else {
last.push(o);
}
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I know for is not as cool as reduce, but its certainly more readable:
const result = [[]];
let current = result[0], currentType = articleBody[0].type;
for(const content of articleBody) {
if(content.type === currentType) {
current.push(content);
} else {
result.push(current = [content]);
currentType = content.type;
}
}
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