0

I have a nested JSON that I'd like to search through using lodash. How can I get the root object from data if a search term I'm looking for is within certain keys, and with one of the keys being dynamic?

For example, if I have:

"data": [
    { 
        "name": "Bob's concourse"
        "activities": [
             {
                 "day": "Monday",
                 "routines":
                     {
                         "Biking": 
                         {
                             "details": "won 3 trophies"
                             "type": "road"
                         },
                         "Kayaking":
                         {
                             "details": "participated in 3 races"
                             "type": "rhythm"
                         }
                      }
                 }
             }
        ]
    },

    {..other_data_etc...},

]
  • activities can be []; it's not guaranteed that it contains any data.
  • routines keys are dynamic. ie, Biking, Kayaking are dynamic strings. It can be anything.

If I want to search for an races (case insensitive), I want to search specifically in:

  • data.name
  • data.activities.routines.* (the dynamic keys)
  • data.activities.routines.*.details

If any one of those matches, then it will return the root object: { "name": "Bob", ..... }

I was able to get the name to return:

function searchText(collection, searchterm) {
    return _.filter(collection, function(o) { 
        return _.includes(o.name.toLowerCase(), searchterm)
    } );
};

But I'm still new to lodash, and I was unable to get any of the nested searches to return correctly, especially with the dynamic keys part.

Could anyone help explain a solution?

tempomax
  • 773
  • 4
  • 10
  • 24

1 Answers1

0

Expanding on your existing attempt with lodash:

const obj = {
  data: [{
    name: 'Bob\'s concourse',
    activities: [{
      day: 'Monday',
      routines: {
        Biking: {
          details: 'won 3 trophies',
          type: 'road'
        },
        Kayaking: {
          details: 'participated in 3 races',
          type: 'rhythm'
        }
      }
    }]
  }]
};

function search(str, data) {
  const searchStr = str.toLowerCase();

  // Only return the entries that contain a matched value
  return _.filter(data, (datum) => {
    // Check if name matches
    return _.includes(datum.name, searchStr)
    || _.some(datum.activities, (activity) => {
        return _.entries(activity.routines).some(([routine, {details}]) => {
          // Check if dynamic routine matches or details
          return _.includes(routine, searchStr) || _.includes(details, searchStr);
        });
      });
  });
}

console.log(search('foobar', obj.data));
console.log(search('races', obj.data));
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.10/lodash.min.js"></script>

You can also accomplish this with plain JavaScript. Using some newish syntax such as destructuring assignment and newish native methods such as Object.entries essentially follows the same pattern as using lodash:

const obj = {
  data: [{
    name: 'Bob\'s concourse',
    activities: [{
      day: 'Monday',
      routines: {
        Biking: {
          details: 'won 3 trophies',
          type: 'road'
        },
        Kayaking: {
          details: 'participated in 3 races',
          type: 'rhythm'
        }
      }
    }]
  }]
};

function search(str, data) {
  const regex = RegExp(str, 'i');

  // Only return the entries that contain a matched value
  return data.filter((datum) => {
    // Check if name matches
    return regex.test(datum.name)
    || datum.activities.some((activity) => {
        return Object.entries(activity.routines).some(([routine, {details}]) => {
          // Check if dynamic routine matches or details
          return regex.test(routine) || regex.test(details);
        });
      });
  });
}

console.log(search('foobar', obj.data));
console.log(search('races', obj.data));
Jason Cust
  • 10,743
  • 2
  • 33
  • 45