I have a JSON file with nested objects, I need to extract a specific key's value from each object and save to an array. No need to preserve the structure or order.
Please see JSON below. I need to extract the values of the 'text' key and have a resulting array like this
["CPUs", "AMD", "Ryzen", "intel",....]
What is the best way to achieve this?
[
{
"itemID":"1",
"items":[
{
"itemID":"2",
"items":[
{
"itemID":"3",
"items":[
{
"itemID":"15",
"text":"Ryzen"
},
{
"itemID":"16",
"text":"Threadripper"
}
],
"text":"AMD",
},
{
"itemID":"66",
"items":[
{
"itemID":"76",
"text":"i5"
},
{
"itemID":"77",
"text":"i7"
},
{
"itemID":"78",
"text":"i3"
}
],
"text":"Intel"
},
{
"itemID":"70",
"text":"Apple"
}
],
"text":"CPUs"
}
],
"text":"computer parts"
},
{
"itemID":"4",
"items":[
{
"itemID":"5",
"items":[
{
"itemID":"21",
"text":"porsche"
},
{
"itemID":"22",
"text":"maserati"
},
{
"itemID":"23",
"text":"ferrari"
}
],
"text":"sports cars"
}
],
"text":"cars"
}
]
3 Answers 3
You can write a recursive function which returns the text value at the current node, then all the text values from its child items:
const data = [{"itemID":"1","items":[{"itemID":"2","items":[{"itemID":"3","items":[{"itemID":"15","text":"Ryzen"},{"itemID":"16","text":"Threadripper"}],"text":"AMD"},{"itemID":"66","items":[{"itemID":"76","text":"i5"},{"itemID":"77","text":"i7"},{"itemID":"78","text":"i3"}],"text":"Intel"},{"itemID":"70","text":"Apple"}],"text":"CPUs"}],"text":"computer parts"},{"itemID":"4","items":[{"itemID":"5","items":[{"itemID":"21","text":"porsche"},{"itemID":"22","text":"maserati"},{"itemID":"23","text":"ferrari"}],"text":"sports cars"}],"text":"cars"}];
const extract = (arr) => arr.reduce((acc, obj) => acc.concat(obj.text, extract(obj.items || [])), [])
out = extract(data);
console.log(out);
Comments
Simple recursion should help to iterate through the array to get the objects with the text property
const data = [{"itemID":"1","items":[{"itemID":"2","items":[{"itemID":"3","items":[{"itemID":"15","text":"Ryzen"},{"itemID":"16","text":"Threadripper"}],"text":"AMD"},{"itemID":"66","items":[{"itemID":"76","text":"i5"},{"itemID":"77","text":"i7"},{"itemID":"78","text":"i3"}],"text":"Intel"},{"itemID":"70","text":"Apple"}],"text":"CPUs"}],"text":"computer parts"},{"itemID":"4","items":[{"itemID":"5","items":[{"itemID":"21","text":"porsche"},{"itemID":"22","text":"maserati"},{"itemID":"23","text":"ferrari"}],"text":"sports cars"}],"text":"cars"}];
function getText(item) {
let result = [];
if (item.text) result.push(item.text);
for (const key in item) {
if (item.hasOwnProperty(key) && typeof item == 'object') result.push(...getText(item[key]));
}
return result;
}
data.forEach(item => console.log(getText(item)));
Comments
Here is an iterative solution using object-scan. To me it's more readable and maintainable than writing a custom recursive function. But adding a dependency is obviously a tradeoff
// const objectScan = require('object-scan');
const data = [{ itemID: '1', items: [{ itemID: '2', items: [{ itemID: '3', items: [{ itemID: '15', text: 'Ryzen' }, { itemID: '16', text: 'Threadripper' }], text: 'AMD' }, { itemID: '66', items: [{ itemID: '76', text: 'i5' }, { itemID: '77', text: 'i7' }, { itemID: '78', text: 'i3' }], text: 'Intel' }, { itemID: '70', text: 'Apple' }], text: 'CPUs' }], text: 'computer parts' }, { itemID: '4', items: [{ itemID: '5', items: [{ itemID: '21', text: 'porsche' }, { itemID: '22', text: 'maserati' }, { itemID: '23', text: 'ferrari' }], text: 'sports cars' }], text: 'cars' }];
const find = objectScan(['**(^items$).text'], {
useArraySelector: false,
rtn: 'value'
});
console.log(find(data));
/* => [
'cars', 'sports cars',
'ferrari', 'maserati',
'porsche', 'computer parts',
'CPUs', 'Apple',
'Intel', 'i3',
'i7', 'i5',
'AMD', 'Threadripper',
'Ryzen'
] */
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/[email protected]"></script>
Disclaimer: I'm the author of object-scan