1

I am using .js and lodash

I need to be able to perform a deep search (depth and keys might vary) in objects. For example:

This is my data set which I need to perform the search:

const data = [
  object1: {
    key1: {
      level1: {
        filter: "this is a value"
      },
      junk: "..."
    },
    key2: {
      level1:{
        filter: ".."
      }
      junk: "this is complicated"
    },
    ...
  },
  object2: { ... },
  object3: { ... }
]

This is my search criteria which includes some properties of the objects in the dataset, and if the value is true I will use that as a filter to filter out the data

const searchCriteria = {
  key1: {
    level1: {
      filter: true,
      someOherFilter: false
    }
  },
  key2: {
    junk: true
  },
  ...
}     

So as you see I cannot depend of the name of the keys, I just have to retrieve all the "true" values from the search criteria, use them as chained filter and search the data set, when they match I return the whole object.

I can find the true values from the searchCriteria using: const selectedFilters = _.keys(_.pickBy(filter, _.identity)) just need to apply the filters to the objects

Now I am left with an array that has the filters: [filter] or [junk]

We are using lodash, I played quite a bit with plucks, and maps, and filters, and finds.. but couldn't get what I needed.

So assuming I get my search criteria as: ["key1", "level1", "filter] or I might get it as ["key2", "junk"].

What's the best way to write the deep search function, preferably using lodash?

Sarah A
  • 1,185
  • 12
  • 27

2 Answers2

0

Solution without lodash:

Here getKeyByPathArr gets 2 params: the objectand an array of key levels, and returns a value or null if key doesn't exist or arguments are bad.

// @param o <object>
// @param pathArr <array> of key levels
// @return o[key] value where key = pathArr.join(.) and value exists, 
// otherwise return null. If pathArr === [] return o

const getKeyByPathArr = o => pathArr => (Array.isArray(pathArr)) 
    ? pathArr.reduce((a, c) => (a[c] != null) ? a[c] : null, o)
    : null;
    
const data = {
  object1: {
    key1: { level1: { filter: "this is a value" } },
    key2: { junk: "this is complicated" },
  },
  object2: {},
  object3: {}
};

const keys1 = ["key1", "level1", "filter"];
const keys2 = ["key2", "junk"];

const result1 = getKeyByPathArr(data.object1)(keys1);
const result2 = getKeyByPathArr(data.object1)(keys2);

console.log(result1)
console.log(result2)
Egor Stambakio
  • 17,836
  • 5
  • 33
  • 35
0

Since your data may be a huge list, you'd better initialize your filters from searchCriteria first.

1 loop over searchCriteria recursively, if the deepest boolean value is true, output the path of that filter:

// getters
`searchCriteria` => [ _.property('key1.level1.filter'), _.property('key1.level1.otherFilter')]

2 use getters in step1 to filter object in data

_.filter(data, obj => _.some(getters, getter => !!getter(obj))) // etc.
maow
  • 1,387
  • 11
  • 20
  • This looks like it should work like a charm, but I get an empty array: _.filter(data, (item)=>{ return _.some([_.property("key1.level1.filter")], (getter)=> { return !!getter(item) } } – Sarah A May 19 '17 at 07:37
  • I guess I know why I am getting an empty list. It could be because form of the properties i.e. level1 might be an array, there fore _.property("key1.level1.filter") won't work since I have to iterate through level1 .. life could have been easier at 3 am! – Sarah A May 19 '17 at 08:03