5

I have a JSON API response with the following structure

[
  {
    title: "top1",
    sections: [
      {
        section_title: "section1",
        content: [
          {
            content_title: "title1",
            content_id: "id1"
          },
          {
            content_title: "title2",
            content_id: "id2"
          }
        ]
      },
      {
        section_title: "section2",
        content: [
          {
            content_title: "title3",
            content_id: "id3"
          },
          {
            content_title: "title4",
            content_id: "id4"
          }
        ]
      }
    ]
  }, {
    title: "top2",
    sections: [...]
  },
  ...
]

I also have a small array of content IDs arr2 = ['id2','id3']. I need to search the data from the API request to find any content_id that is contained in arr2.

I have some working lodash code, but my approach of nested forEach does not seem to be the most efficient approach:

_.forEach(response, function(top) {
  _.forEach(top.sections, function(section) {
    _.forEach(section.content, function(content) {
      _.forEach(arr2, function(id) {
        if(id === content.content_id) {
         // Do stuff
        }
      })
    })
  })
})

How could I improve this code?

2
  • I think it might help to give an example of the //Do stuff that you want to achieve. Do you want to mutate the API response in some way, or create a new object based on the content objects matching. Your iteration is fine if you want to do something for each and every matching object, although _.find() could help to remove a loop. Commented Jul 27, 2017 at 19:30
  • If the comparison is true, I will set a property to true on the found content object, a property to true on the parent section object and a property to true on the grandparent top object: content.owned = true, section.owned = true, top.owned = true
    – mjohnston
    Commented Jul 27, 2017 at 19:33

2 Answers 2

1

After a little thought, I actually can't come up with a more elegant solution using other lodash functions for you. It seems that to set the owned properties for each and every case the forEach is the way to go. The only optimisation I can make is to avoid using lodash simply by using the vanilla javascript forEach Array function, and use find to replace the innermost forEach (might make a slight performance improvement).

response.forEach((topItem) => {
    topItem.sections.forEach((section) => {
        section.content.forEach((content) => {
            if(arr2.find((item) => { return item === content.content_id; })){
                topItem.owned = true; section.owned = true; content.owned = true;
            }
        });
    });
});

I have a personal preference for arrow function syntax also...

1

You can create a function that recursively iterates over recognized descendant properties.

function deepWalk(collection, childKeys, iteratee) {

  // create a partial _.each with an iterator that will
  // recursively traverse properties from the `childKeys` array
  var each = _.partial(_.each, _, function(value, index) {
    // invoke iteratee callback
    iteratee(value, index);
    // only recursively iterate over properties found in childKeys
    _(value).pick(childKeys).each(each);
  });

  // invoke iteration
  each(collection);

}

deepWalk(collection, ['sections', 'content'], function(value) {
  if(_.includes(['id2', 'id3'], value.content_id)) {
    // do whatever you want here..
    console.log(value);
  }
});

var collection = [
  {
    title: "top1",
    sections: [
      {
        section_title: "section1",
        content: [
          {
            content_title: "title1",
            content_id: "id1"
          },
          {
            content_title: "title2",
            content_id: "id2"
          }
        ]
      },
      {
        section_title: "section2",
        content: [
          {
            content_title: "title3",
            content_id: "id3"
          },
          {
            content_title: "title4",
            content_id: "id4"
          }
        ]
      }
    ]
  }
];

function deepWalk(collection, childKeys, iteratee) {
  
  // create a partial _.each with an iterator that will
  // recursively traverse properties from the `childKeys` array
  var each = _.partial(_.each, _, function(value, index) {
    // invoke iteratee callback
    iteratee(value, index);
    // only recursively iterate over properties found in childKeys
    _(value).pick(childKeys).each(each);
  });
  
  // invoke iteration
  each(collection);
  
}

deepWalk(collection, ['sections', 'content'], function(value) {
  if(_.includes(['id2', 'id3'], value.content_id)) {
    // do whatever you want here..
    console.log(value);
  }
});
.as-console-wrapper { min-height: 100%; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.