2

I am trying to solve the following problem with LoDash. I know how to solve the problem using for loops, but am looking for the modern functional method of solving the problem.


I have the following data:

const data = {
    people: [
        {
            name: "Bob",
            vehicles: [
                {
                    model: "Mazda"
                    tires: [
                        { pressure: 20 },
                        { pressure: 22 },
                        { pressure: 23 },
                        { pressure: 21 },
                    ]
                },
                {
                    model: "Harley Davidson"
                    tires: [
                        { pressure: 20 }
                        { pressure: 25 }
                    ]
                }
            ]
        },
        {...},
        {...},
        {...},
    ]
}

From it, I want to extract a list of every tire pressure belonging to every person. So something like this:

[20, 22, 23, 21, 20, 25, ... ]

What I am looking for is a method that I can call like this:

const path = 'people.vehicles[*].tires[*].pressure';
const tirePressure = _.methodName(data, path);

I know that lodash has support for some similar functionality - like _.at(object, ['a[0].b.c', 'a[1]']); (link), but it doesn't support collapsing the entire array, as far as I can tell.

sixtyfootersdude
  • 25,859
  • 43
  • 145
  • 213

4 Answers4

1

You can achieve that using a Lodash's flatMap. This could probably get shorter using flatMapDeep but this way it's much clearer IMHO.

const data = {
    people: [
        {
            name: "Bob",
            vehicles: [
                {
                    model: "Mazda",
                    tires: [
                        { pressure: 20 },
                        { pressure: 22 },
                        { pressure: 23 },
                        { pressure: 21 },
                    ]
                },
                {
                    model: "Harley Davidson",
                    tires: [
                        { pressure: 20 },
                        { pressure: 25 }
                    ]
                }
            ]
        }
    ]
};


const pressures = _(data.people)
  .flatMap(_.property('vehicles'))
  .flatMap(_.property('tires'))
  .map(_.property('pressure'))
  .value();

console.log(pressures);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js"></script>
jannis
  • 4,843
  • 1
  • 23
  • 53
0

You could take a classical iterative and recursive approach and return only the found values of pressure.

function getPressures(o) {
    return [].concat(...Object.entries(o).map(([key, value]) =>
        key === 'pressure'
            ? value
            : value && typeof value === 'object'
                ? getPressures(value)
                : []
    ));
}

var data = { people: [{ name: "Bob", vehicles: [{ model: "Mazda", tires: [{ pressure: 20 }, { pressure: 22 }, { pressure: 23 }, { pressure: 21 }] }, { model: "Harley Davidson", tires: [{ pressure: 20 }, { pressure: 25 }] }] }] };

console.log(getPressures(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }

A different approach is to implement an get method for a path with works with wildcards as well the result is an object with the pathes as keys and the wanted values.

function get(object, path) {

    function iter(o, p, i) {
        if (i === parts.length) {
            result[p.join('.')] = o;
            return;
        }
        if (!o || typeof o !== 'object') {
            return;
        }
        if (parts[i] === '*') {
            Object
                .entries(o)
                .forEach(([k, v]) => iter(v, p.concat(k), i + 1));
            return;
        }
        if (parts[i] in o) {
            iter(o[parts[i]], p.concat(parts[i]), i + 1);
        }
    }

    var result = {},
        parts = path.split('.');

    iter(object, [], 0);
    return result;
}

var data = { people: [{ name: "Bob", vehicles: [{ model: "Mazda", tires: [{ pressure: 20 }, { pressure: 22 }, { pressure: 23 }, { pressure: 21 }] }, { model: "Harley Davidson", tires: [{ pressure: 20 }, { pressure: 25 }] }] }] },
    result = get(data, 'people.*.vehicles.*.tires.*.pressure'),
    values = Object.values(result);

console.log(result);
console.log(values);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

I'd have done with simple JS like

Use .map to get the tire values and then flatten this array

const data = {
    people: [
        {
            name: "Bob",
            vehicles: [
                {
                    model: "Mazda",
                    tires: [
                        { pressure: 20 },
                        { pressure: 22 },
                        { pressure: 23 },
                        { pressure: 21 },
                    ]
                },
                {
                    model: "Harley Davidson",
                    tires: [
                        { pressure: 20 },
                        { pressure: 25 }
                    ]
                }
            ]
        }
    ]
}
var x=  data.people[0].vehicles.map(o=> o.tires.map(i=> i.pressure))

console.log(x.join(",").split(","))
Muhammad Usman
  • 10,039
  • 22
  • 39
0

In order to extract a list of every tire pressure belonging to every person, you can use reduce and group all the tire pressure values to respective name, and use object data structure to store the output:

const data={people:[{name:"Bob",vehicles:[{model:"Mazda",tires:[{pressure:20},{pressure:22},{pressure:23},{pressure:21}]},{model:"Harley Davidson",tires:[{pressure:20},{pressure:25}]}]}]};

const result = data.people.reduce((all, {name, vehicles}) => {

  const pressures  = vehicles.reduce((total, {tires}) => {
    tires.forEach(({pressure}) => total.push(pressure));
    return total;
  }, []);

  all[name] = pressures;

  return all;

}, {});

console.log(result);
Leonid Pyrlia
  • 1,594
  • 2
  • 11
  • 14