Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use lodash to get a value out of a complex array of objects?

So i'm getting this pretty complex array from an API with lots of nested arrays with objects etc. It looks like this:

    public data: any[] = [
    {
        language: 'Dutch',
        sources: [
            {
                source: 'De Redactie',
                channels: [
                    { channel: 'binnenland', value: false },
                    { channel: 'buitenland', value: false },
                    { channel: 'sport', value: false },
                    { channel: 'cultuur en media', value: false },
                    { channel: 'politiek', value: false },
                    { channel: 'financien', value: false }
                ]
            },
            {
                source: 'Tweakers',
                channels: [
                    { channel: 'hardware', value: false },
                    { channel: 'software', value: false },
                    { channel: 'tech', value: false },
                    { channel: 'smartphones', value: false },
                    { channel: 'audio', value: false },
                    { channel: 'video', value: false }
                ]
            }
        ]
    },
    {
        language: 'English',
        sources: [
            {
                source: 'BBC',
                channels: [
                    { channel: 'news', value: false },
                    { channel: 'sport', value: false },
                    { channel: 'weather', value: false },
                    { channel: 'travel', value: false },
                    { channel: 'politics', value: false }
                ]
            },
            {
                source: 'Fox News',
                channels: [
                    { channel: 'u.s.', value: false },
                    { channel: 'world', value: false },
                    { channel: 'opinion', value: false },
                    { channel: 'politics', value: false },
                    { channel: 'entertainment', value: false },
                    { channel: 'business', value: false }
                ]
            }
        ]
    }
]

And i can extract the value that i want (the channel property) with this function:

setChannel(channel: string, source: string, language: string) {
    for (const i of this.data) {
        if (i.language === language) {
            for (const j of i.sources) {
                if (j.source === source) {
                    for (const k of j.channels) {
                        if (k.channel === channel) {
                            console.log(k.channel);
                        }
                    }
                }
            }
        }
    }
}

Now i'm sure that there is a function in lodash to simplify this. Because triple nested for's and if's is not a nice way of coding. But i can't seem to find it in the documentation. Could someone please point me in the right direction?

like image 420
Martijn van den Bergh Avatar asked Dec 12 '17 08:12

Martijn van den Bergh


2 Answers

Lodash's filter() and flatMap() will get you there:

const result = _(data).filter({language: 'Dutch'})
                      .flatMap('sources')
                      .filter({source: 'De Redactie'})
                      .flatMap('channels')
                      .find({channel: 'sport'});

const data = [
{
    language: 'Dutch',
    sources: [
        {
            source: 'De Redactie',
            channels: [
                { channel: 'binnenland', value: false },
                { channel: 'buitenland', value: false },
                { channel: 'sport', value: false },
                { channel: 'cultuur en media', value: false },
                { channel: 'politiek', value: false },
                { channel: 'financien', value: false }
            ]
        },
        {
            source: 'Tweakers',
            channels: [
                { channel: 'hardware', value: false },
                { channel: 'software', value: false },
                { channel: 'tech', value: false },
                { channel: 'smartphones', value: false },
                { channel: 'audio', value: false },
                { channel: 'video', value: false }
            ]
        }
    ]
},
{
    language: 'English',
    sources: [
        {
            source: 'BBC',
            channels: [
                { channel: 'news', value: false },
                { channel: 'sport', value: false },
                { channel: 'weather', value: false },
                { channel: 'travel', value: false },
                { channel: 'politics', value: false }
            ]
        },
        {
            source: 'Fox News',
            channels: [
                { channel: 'u.s.', value: false },
                { channel: 'world', value: false },
                { channel: 'opinion', value: false },
                { channel: 'politics', value: false },
                { channel: 'entertainment', value: false },
                { channel: 'business', value: false }
            ]
        }
    ]
}
]

const result = _(data).filter({language: 'Dutch'})
                      .flatMap('sources')
                      .filter({source: 'De Redactie'})
                      .flatMap('channels')
                      .filter({channel: 'sport'})
                      .first();

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
like image 196
Robby Cornelissen Avatar answered Oct 27 '22 20:10

Robby Cornelissen


That's actually a pretty "quick & dirty" solution, because I didn't use any fallback default values. But you can read the docs about .find and .get.

const data = [{
    language: 'Dutch',
    sources: [{
        source: 'De Redactie',
        channels: [{
            channel: 'binnenland',
            value: false
          },
          {
            channel: 'buitenland',
            value: false
          },
          {
            channel: 'sport',
            value: false
          },
          {
            channel: 'cultuur en media',
            value: false
          },
          {
            channel: 'politiek',
            value: false
          },
          {
            channel: 'financien',
            value: false
          }
        ]
      },
      {
        source: 'Tweakers',
        channels: [{
            channel: 'hardware',
            value: false
          },
          {
            channel: 'software',
            value: false
          },
          {
            channel: 'tech',
            value: false
          },
          {
            channel: 'smartphones',
            value: false
          },
          {
            channel: 'audio',
            value: false
          },
          {
            channel: 'video',
            value: false
          }
        ]
      }
    ]
  },
  {
    language: 'English',
    sources: [{
        source: 'BBC',
        channels: [{
            channel: 'news',
            value: false
          },
          {
            channel: 'sport',
            value: false
          },
          {
            channel: 'weather',
            value: false
          },
          {
            channel: 'travel',
            value: false
          },
          {
            channel: 'politics',
            value: false
          }
        ]
      },
      {
        source: 'Fox News',
        channels: [{
            channel: 'u.s.',
            value: false
          },
          {
            channel: 'world',
            value: false
          },
          {
            channel: 'opinion',
            value: false
          },
          {
            channel: 'politics',
            value: false
          },
          {
            channel: 'entertainment',
            value: false
          },
          {
            channel: 'business',
            value: false
          }
        ]
      }
    ]
  }
];

const setChannel = (channel = '', source = '', language = '') => {
  // get all sources for the given language
  const sourcesForLanguage = _.get(_.find(data, ['language', language]), 'sources');
  // get all channels for given source
  const channelsForSource = _.get(_.find(sourcesForLanguage, ['source', source]), 'channels');
  // get the channel object for given channel
  const selectedChannel = _.find(channelsForSource, ['channel', channel]);
  console.log(selectedChannel);
}

setChannel('news', 'BBC', 'English');
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
like image 23
Ramiz Wachtler Avatar answered Oct 27 '22 18:10

Ramiz Wachtler