Caio Tarifa
Caio Tarifa

Reputation: 6043

Return multiple elements using Lodash _.get?

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

Answers (5)

det
det

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

vincent
vincent

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

sstruct
sstruct

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

Ori Drori
Ori Drori

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

CertainPerformance
CertainPerformance

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

Related Questions