Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine _.map and _.filter in a more efficient way?

I am using Lodash in my Angular project and I was wondering if there is a better way to write the following code:

$scope.new_arr = _.map(arr1, function(item){
    return _.assign(item, {new_id: _.find(arr2, {id: item.id})});
  });
$scope.new_arr = _.filter($scope.new_arr, function (item) {
  return item.new_id !== undefined;
});

I am trying to combine values from one array to same objects in other array, and I want to ignore the objects that not appear in both arrays (it is something like join or left outer join in the sql language).

Here is a fiddle with an example of this code: Click me!

like image 298
Vali D Avatar asked Jan 15 '17 09:01

Vali D


3 Answers

i think is better to use chaining

$scope.new_arr = _.chain(arr1)
    .map(function(item) {
        return _.merge(
            {}, // to avoid mutations
            item, 
            {new_id: _.find(arr2, {id: item.id})}
        ); 
    })
    .filter('new_id')
    .value();
like image 132
stasovlas Avatar answered Nov 13 '22 11:11

stasovlas


https://jsfiddle.net/3xjdqsjs/6/

try this:

  $scope.getItemById = (array, id) => {
    return array.find(item => item.id == id);
  };

  $scope.mergeArrays = () => {
   let items_with_ids = arr1.filter(item => !_.isNil($scope.getItemById(arr2,item.id)));
   return items_with_ids.map(item => _.assign(item, {new_id: $scope.getItemById(arr2,item.id)}));
  };
like image 41
YairTawil Avatar answered Nov 13 '22 09:11

YairTawil


The answers provided here are all runtime of O(n^2), because they first run an outer loop on the first array, with an inner loop on the second array. You can instead run this in O(n). First, create a hashmap of all the ids in arr2 in a single loop; this will allow us an order 1 lookup. In the second loop on arr1, check this hashmap to determine if those items exist with O(n). Total Complexity is n + n = 2n, which is just O(n).

// provision some test arrays
var arr1 = [
    {
        id: 2
    },
    {
        id: 4
    },
    {
        id: 6
    }
]

var arr2 = [
    {
        id: 3
    },
    {
        id: 4
    },
    {
        id: 5
    },
    {
        id: 6
    }
]

// First, we create a map of the ids of arr2 with the items. Complexity: O(n)
var mapIdsToArr2Items = _.reduce(arr2, function(accumulator, item) {
    accumulator[item.id] = item;
    return accumulator;
}, {});

// Next, we use reduce (instead of a _.map followed by a _.filter for slightly more performance.
// This is because with reduce, we loop once, whereas with map and filter, 
// we loop twice). Complexity: O(n)
var combinedArr = _.reduce(arr1, function(accumulator, item) {

    // Complexity: O(1)
    if (mapIdsToArr2Items[item.id]) {
        // There's a match/intersection! Arr1's item matches an item in arr 2. Include it
        accumulator.push(item);
    }
    return accumulator;    
}, []);

console.log(combinedArr)
like image 34
Prith Avatar answered Nov 13 '22 11:11

Prith