Reputation: 6043
I have this JavaScript object:
const object = {
categories: [
{
title: 'Category 1',
items: [
{ title: 'Item 1', image: 'path/to/file-1.png' },
{ title: 'Item 2', image: 'path/to/file-2.png' },
{ title: 'Item 3', image: 'path/to/file-3.png' }
]
},
{
title: 'Category 2',
items: [
{ title: 'Item 4', image: 'path/to/file-4.png' },
{ title: 'Item 5', image: 'path/to/file-5.png' },
{ title: 'Item 6', image: 'path/to/file-6.png' }
]
}
]
}
I was able to select only one item using Lodash's _.get
method.
_.get(object, 'categories[0].items[0].image')
// => "path/to/file-1.png"
But I need an array with all occurrences, not just from position 0. Something like this:
_.get(object, 'categories[].items[].image')
// => ["path/to/file-1.png", "path/to/file-2.png", "path/to/file-3.png", "path/to/file-4.png", "path/to/file-5.png", "path/to/file-6.png"]
Is it possible to do it using Lodash?
If it's not possible, do you have any idea how to implement it?
EDIT: I'm looking for something like _.get
from Lodash, where I can supply the "search schema" (path) as string. Obviously I know how to solve this with map
, reduce
, etc.
Upvotes: 0
Views: 1355
Reputation: 21
_([object]).flatMap("categories").flatMap("items").flatMap("image").value()
Full example:
const _ = lodash; // const _ = require("lodash");
const object = {
categories: [
{
title: 'Category 1',
items: [
{ title: 'Item 1', image: 'path/to/file-1.png' },
{ title: 'Item 2', image: 'path/to/file-2.png' },
{ title: 'Item 3', image: 'path/to/file-3.png' }
]
},
{
title: 'Category 2',
items: [
{ title: 'Item 4', image: 'path/to/file-4.png' },
{ title: 'Item 5', image: 'path/to/file-5.png' },
{ title: 'Item 6', image: 'path/to/file-6.png' }
]
}
]
};
const result = _([object]).flatMap("categories").flatMap("items").flatMap("image").value();
console.log(result);
<script src="https://bundle.run/[email protected]"></script>
Upvotes: 0
Reputation: 2181
We use object-scan for these types of queries now. It's pretty powerful (once you wrap your head around it) and easy enough to use for basic stuff. Here is how you could solve your problem
// const objectScan = require('object-scan');
const object = { categories: [ { title: 'Category 1', items: [ { title: 'Item 1', image: 'path/to/file-1.png' }, { title: 'Item 2', image: 'path/to/file-2.png' }, { title: 'Item 3', image: 'path/to/file-3.png' } ] }, { title: 'Category 2', items: [ { title: 'Item 4', image: 'path/to/file-4.png' }, { title: 'Item 5', image: 'path/to/file-5.png' }, { title: 'Item 6', image: 'path/to/file-6.png' } ] } ] };
const r = objectScan(['categories[*].items[*].image'], { rtn: 'value' })(object);
console.log(r);
/* =>
[ 'path/to/file-6.png', 'path/to/file-5.png', 'path/to/file-4.png',
'path/to/file-3.png', 'path/to/file-2.png', 'path/to/file-1.png' ]
*/
.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
Upvotes: 1
Reputation: 491
Here is my implementation which can handle []
path:
const object = { categories: [ { title: "Category 1", items: [ { title: "Item 1", image: "path/to/file-1.png" }, { title: "Item 2", image: "path/to/file-2.png" }, { title: "Item 3", image: "path/to/file-3.png" } ] }, { title: "Category 2", items: [ { title: "Item 4", image: "path/to/file-4.png" }, { title: "Item 5", image: "path/to/file-5.png" }, { title: "Item 6", image: "path/to/file-6.png" } ] } ] };
const object2 = { categories: { title: "Category 1" } };
function flatGet(object, path) {
const pathArray = path.split(".");
while (pathArray.length > 0) {
let pos = pathArray.shift();
if (pos.includes("[]")) {
pos = pos.slice(0, -2);
}
if (Array.isArray(object)) {
object = object.reduce((acc, cur) => acc.concat(cur[pos]), []);
} else {
object = object[pos];
}
}
return object;
}
var res = flatGet(object, "categories[].items[].image"); // ["path/to/file-1.png", ...]
var res2 = flatGet(object2, "categories.title"); // "Category 1"
edited before:
Cause you need to get the images
Array, map
could help with it. Here is a simple one:
_.map(object.categories[0].items, o => _.get(o, 'image'))
// if categories[0].items is not always there, just use another get:
_.map(_.get(object, 'categories[0].items'), o => _.get(o, 'image'))
Upvotes: -1
Reputation: 191976
This is my implementation of a flatGet
function that can handle arrays:
const flatGet = (object, path) => {
const p = path.match(/[^.\[\]]+/g)
const getItem = (item, [current, ...path]) => {
if(current === undefined) return item
if(typeof item !== 'object') return undefined
if(Array.isArray(item)) return item.flatMap(o => getItem(o[current], path))
return getItem(item[current], path)
}
return getItem(object, p)
}
const object = {"categories":[{"title":"Category 1","items":[{"title":"Item 1","image":"path/to/file-1.png"},{"title":"Item 2","image":"path/to/file-2.png"},{"title":"Item 3","image":"path/to/file-3.png"}]},{"title":"Category 2","items":[{"title":"Item 4","image":"path/to/file-4.png"},{"title":"Item 5","image":"path/to/file-5.png"},{"title":"Item 6","image":"path/to/file-6.png"}]}]}
var result = flatGet(object, 'categories.items.image')
console.log(result)
Upvotes: 1
Reputation: 370789
This is pretty easy to do with flatMap
, no library required:
const object = {
categories: [
{
title: 'Category 1',
items: [
{ title: 'Item 1', image: 'path/to/file-1.png' },
{ title: 'Item 2', image: 'path/to/file-2.png' },
{ title: 'Item 3', image: 'path/to/file-3.png' }
]
},
{
title: 'Category 2',
items: [
{ title: 'Item 4', image: 'path/to/file-4.png' },
{ title: 'Item 5', image: 'path/to/file-5.png' },
{ title: 'Item 6', image: 'path/to/file-6.png' }
]
}
]
};
const images = object.categories.flatMap(({ items }) => items.map(({ image }) => image));
console.log(images);
Or, with reduce
:
const object = {
categories: [
{
title: 'Category 1',
items: [
{ title: 'Item 1', image: 'path/to/file-1.png' },
{ title: 'Item 2', image: 'path/to/file-2.png' },
{ title: 'Item 3', image: 'path/to/file-3.png' }
]
},
{
title: 'Category 2',
items: [
{ title: 'Item 4', image: 'path/to/file-4.png' },
{ title: 'Item 5', image: 'path/to/file-5.png' },
{ title: 'Item 6', image: 'path/to/file-6.png' }
]
}
]
};
const images = object.categories.reduce((a, { items }) => {
items.forEach(({ image }) => {
a.push(image);
});
return a;
}, []);
console.log(images);
Upvotes: 2