Grant Vinson
Grant Vinson

Reputation: 217

Filter JS multidimensional array

I am attempting to filter some events and their corresponding time slots. The data structure is like so:

var items = [
  {
    title: 'Category Title',
    events: [
      {
        title : '1:00pm',
        category : 1
      },
      {
        title: '2:00pm',
        category : 2
      }
    ]
  },
  {
    title: 'Category Title 2',
    events: [
      {
        title : '3:00pm',
        category : 1
      },
      {
        title: '4:00pm',
        category : 2
      }
    ]
  }
];

I am wanting to filter events by their category. I've tried using filters/maps but can't seem to figure it out. For example, how would I filter the list to only return items with the category of 1?

Upvotes: 2

Views: 6772

Answers (5)

Ele
Ele

Reputation: 33726

You can use the function filter along with the function some to get those items with a specific category within the array events.

This example has an item which events array contains a category === 3

var items = [  {    title: 'Category Title',    events: [      {        title : '1:00pm',        category : 1      },      {        title: '2:00pm',        category : 2      }    ]  },  {    title: 'Category Title 2',    events: [      {        title : '3:00pm',        category : 1      },      {        title: '4:00pm',        category : 2      }    ]  },  {    title: 'Category Title 3',    events: [      {        title : '3:00pm',        category : 1      },      {        title: '4:00pm',        category : 3      }    ]  }],
    cat = 3,
    result = items.filter(o => o.events.some(({category}) => cat === category));

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

If you need to get only the events with category === 3 use the function reduce:

var items = [  {    title: 'Category Title',    events: [      {        title : '1:00pm',        category : 1      },      {        title: '2:00pm',        category : 2      }    ]  },  {    title: 'Category Title 2',    events: [      {        title : '3:00pm',        category : 1      },      {        title: '4:00pm',        category : 2      }    ]  },  {    title: 'Category Title 3',    events: [      {        title : '3:00pm',        category : 1      },      {        title: '4:00pm',        category : 3      }    ]  }],
    cat = 3,
    result = items.reduce((a/*Accumulator*/, o/*Current object in array*/) => { 
      // 1. We get the events with a specific category  (In this case -> 3)
      var filtered = o.events.filter(({category}) => cat === category);
      
      // 2. If the previous filter returns at least one event, we need to push
      //    the current object 'o' with the filtered events.
      if (filtered.length) { // if length !== 0 we need to push the object.
        // 3. We use Object.assign to avoid any mutation over the original object.
        //    So, basically we create a new object with the original properties
        //    and finally we assign the filtered events to the property events.
        a.push(Object.assign({}, o, {events: filtered}));
      }
      
      return a;
    }, []/*Initial value (this array will contain the filtered objects)*/);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 2

Joe Warner
Joe Warner

Reputation: 3452

you can use filter to get the reduced list but you can do this in other ways like reduce.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

var items = [
  {
    title: 'Category Title',
    events: [
      {
        title : '1:00pm',
        category : 1
      },
      {
        title: '2:00pm',
        category : 2
      }
    ]
  },
  {
    title: 'Category Title 2',
    events: [
      {
        title : '3:00pm',
        category : 1
      },
      {
        title: '4:00pm',
        category : 2
      }
    ]
  }
];
let cat1, cat2;
items.forEach(item => {
  cat1 = item.events.filter(item => item.category === 1)
  cat2 = item.events.filter(item => item.category === 2)
})


console.log(cat1, cat2)

Heres a bit more advanced you can give the function an array of categories you want and it will return an object with keys of all the categories and the events inside them. Hope this helps.

var items = [
      {
        title: 'Category Title',
        events: [
          {
            title : '1:00pm',
            category : 1
          },
          {
            title: '2:00pm',
            category : 2
          }
        ]
      },
      {
        title: 'Category Title 2',
        events: [
          {
            title : '3:00pm',
            category : 1
          },
          {
            title: '4:00pm',
            category : 2
          }
        ]
      }
    ];
    
    function filter(cat, arr) {
      return arr.filter(i => i.category === cat);
    }
    function getCategorys(category) {
      return items.reduce((prev, curr) => {
         category.forEach(cat => {
         !prev[cat] ? prev[cat] = [] : null;
         prev[cat] = [...filter(cat, curr.events), ...prev[cat]];
         })
         return prev;
      }, {})
    }
    
    console.log(getCategorys([1,2]))

Upvotes: 1

cнŝdk
cнŝdk

Reputation: 32145

What you should do here is to use Array.filter() method to filter your items array, based on the sub events array, which you can test upon with Array.some() to find those with category===1.

var filteredResults = items.filter(function(item) {
  return item.events.some(e => e.category === 1);
});

Demo:

var items = [{
    title: 'Category Title',
    events: [{
        title: '1:00pm',
        category: 1
      },
      {
        title: '2:00pm',
        category: 2
      }
    ]
  },
  {
    title: 'Category Title 2',
    events: [{
        title: '3:00pm',
        category: 1
      },
      {
        title: '4:00pm',
        category: 2
      }
    ]
  }
];

var result = items.filter(function(item) {
  return item.events.some(e => e.category === 1);
});


console.log(result);

Upvotes: 1

Z2VvZ3Vp
Z2VvZ3Vp

Reputation: 7593

function hasCategory(item, cat) {
    for(e in item.events){
    if(item.events[e].category === cat) {
       return true;
     break;
    }
   }
   return false;
}

var filtered = items.filter((item) => {
    return hasCategory(item, 2);
});

console.log(filtered);

Upvotes: -1

Dmitrijs Balcers
Dmitrijs Balcers

Reputation: 157

You can use the following functions to get desired result:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

function filterByCategory(items, category) {
    return items.reduce((events, i) => {
        const filteredEvents = i.events.filter(e => e.category === category);
        if (filteredEvents) {
            return events.concat(filteredEvents);
        }
        return events;
    }, []);
}

Upvotes: 0

Related Questions