14

In an array of objects I need to find a value -- where key is activity : However the activity key can be deeply nested in the array like so:

const activityItems = [
    {
        name: 'Sunday',
        items: [
            {
                name: 'Gym',
                activity: 'weights',
            },
        ],
    },
    {
        name: 'Monday',
        items: [
            {
                name: 'Track',
                activity: 'race',
            },
            {
                name: 'Work',
                activity: 'meeting',
            },
            {
                name: 'Swim',
                items: [
                    {
                        name: 'Beach',
                        activity: 'scuba diving',
                    },
                    {
                        name: 'Pool',
                        activity: 'back stroke',
                    },
                ],
            },
        ],    
    },
    {} ...
    {} ...
];

So I wrote a recursive algorithm to find out if a certain activity is in the array:

let match = false;
const findMatchRecursion = (activity, activityItems) => {
    for (let i = 0; i < activityItems.length; i += 1) {
        if (activityItems[i].activity === activity) {
            match = true;
            break;
        }

        if (activityItems[i].items) {
            findMatchRecursion(activity, activityItems[i].items);
        }
    }

    return match;
};

Is there an ES6 way of determining if an activity exists in an array like this?

I tried something like this:

const findMatch(activity, activityItems) {
    let obj = activityItems.find(o => o.items.activity === activity);
    return obj;
}

But this won't work with deeply nested activities.

Thanks

4 Answers 4

10

You can use some() method and recursion to find if activity exists on any level and return true/false as result.

const activityItems = [{"name":"Sunday","items":[{"name":"Gym","activity":"weights"}]},{"name":"Monday","items":[{"name":"Track","activity":"race"},{"name":"Work","activity":"meeting"},{"name":"Swim","items":[{"name":"Beach","activity":"scuba diving"},{"name":"Pool","activity":"back stroke"}]}]}]

let findDeep = function(data, activity) {
  return data.some(function(e) {
    if(e.activity == activity) return true;
    else if(e.items) return findDeep(e.items, activity)
  })
}

console.log(findDeep(activityItems, 'scuba diving'))

Sign up to request clarification or add additional context in comments.

Comments

4

While not as elegant as a recursive algorithm, you could JSON.stringify() the array, which gives this:

[{"name":"Sunday","items":[{"name":"Gym","activity":"weights"}]},{"name":"Monday","items":[{"name":"Track","activity":"race"},{"name":"Work","activity":"meeting"},{"name":"Swim","items":[{"name":"Beach","activity":"scuba diving"},{"name":"Pool","activity":"back stroke"}]}]}]

You could then use a template literal to search for the pattern:

`"activity":"${activity}"`

Complete function:

findMatch = (activity, activityItems) =>
  JSON.stringify(activityItems).includes(`"activity":"${activity}"`);

const activityItems = [{
    name: 'Sunday',
    items: [{
      name: 'Gym',
      activity: 'weights',
    }, ],
  },
  {
    name: 'Monday',
    items: [{
        name: 'Track',
        activity: 'race',
      },
      {
        name: 'Work',
        activity: 'meeting',
      },
      {
        name: 'Swim',
        items: [{
            name: 'Beach',
            activity: 'scuba diving',
          },
          {
            name: 'Pool',
            activity: 'back stroke',
          },
        ],
      },
    ],
  }
];

findMatch = (activity, activityItems) =>
  JSON.stringify(activityItems).includes(`"activity":"${activity}"`);

console.log(findMatch('scuba diving', activityItems)); //true
console.log(findMatch('dumpster diving', activityItems)); //false

Comments

1

First, your function could be improved by halting once a match is found via the recursive call. Also, you're both declaring match outside, as well as returning it. Probably better to just return.

const findMatchRecursion = (activity, activityItems) => {
    for (let i = 0; i < activityItems.length; i += 1) {
        if (activityItems[i].activity === activity) {
            return true;
        }

        if (activityItems[i].items && findMatchRecursion(activity, activityItems[i].items) {
            return true;
        }
    }

    return false;
};

There's no built in deep search, but you can use .find with a named function if you wish.

var result = !!activityItems.find(function fn(item) {
  return item.activity === "Gym" || (item.items && item.items.find(fn));
});

1 Comment

Unless I'm missing something, this returns the top-most parent that contains the object searched for, not the object itself.
1

We now use object-scan for simple data processing tasks like this. It's really good once you wrap your head around how to use it. Here is how one could answer your questions

// const objectScan = require('object-scan');

const find = (activity, input) => objectScan(['**'], {
  abort: true,
  rtn: 'value',
  filterFn: ({ value }) => value.activity === activity
})(input);

const activityItems = [{"name":"Sunday","items":[{"name":"Gym","activity":"weights"}]},{"name":"Monday","items":[{"name":"Track","activity":"race"},{"name":"Work","activity":"meeting"},{"name":"Swim","items":[{"name":"Beach","activity":"scuba diving"},{"name":"Pool","activity":"back stroke"}]}]}]

console.log(find('scuba diving', activityItems));
// => { name: 'Beach', activity: 'scuba diving' }
.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

10 Comments

Im trying to use this lib, but getting this error. could you please help check ? "<a class='gotoLine' href='#45:33'>45:33</a> Uncaught TypeError: Cannot read properties of null (reading 'escalationId')" JSFiddle to the code and data json jsfiddle.net/ksgsharma/59hkdmjb
@SharmaK sure, here you go jsfiddle.net/jLg3kpd6
follow up question, from the attached jsfiddle data want to find all the elements that have "referenceField: true"
created a new question on stackoverflow. Thank you. stackoverflow.com/questions/71113456/…
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.