mickzer
mickzer

Reputation: 6338

Mapping 2 related arrays together

I'm looking for the cleanest and coolest way to merge related arrays together in JavaScript.

My example is this:

I get two JSON arrays from my API: Issues and Locations.

Issues have a location_id and as a result I want to give each Issue a location field which has the correct location object depending on the Issue's location_id.

If I had this data:

var issues = [{id: 1, title: 'issue 1', location_id: 1}, {id: 12, title: 'issue 1', location_id: 2}];

var locations = [{id: 1, name: 'location 1'}, {id: 2, name: 'location 2'}];

The ugly solution would be:

for(i = 0; i < issues.length; ++i) {
    for(j = 0; j < locations.length; ++j) {
        if(issues[i].location_id  == locations[j].id) {
            issues[i].location = locations[j];
            break;
        }
    }
}

The resulting issues array would be:

[[object Object] {
    id: 1,
    location: [object Object] {
        id: 1,
        name: "location 1"
     },
     location_id: 1,
     title: "issue 1"
}, [object Object] {
    id: 12,
    location: [object Object] {
        id: 2,
        name: "location 2"
     },
     location_id: 2,
     title: "issue 1"
}]

I was trying (and failing) to come up with a shorter solution or even a one liner using .map().

Any guidance appreciated!! :)

Upvotes: 1

Views: 61

Answers (4)

Andy
Andy

Reputation: 63524

Use map and filter:

issues.map(function (issue) {
    issue.location = locations.filter(function(location) {
      return issue.location_id === location.id;
    })[0];
    return issue;
});

Upvotes: 3

fuyushimoya
fuyushimoya

Reputation: 9813

Use an object to keep the map, then time complexity will be O(n+m) instead of O(n*m).

var issues = [{id: 1, title: 'issue 1', location_id: 1}, {id: 12, title: 'issue 1', location_id: 2}];
var locations = [{id: 1, name: 'location 1'}, {id: 2, name: 'location 2'}];

var binds = function(locationList, issueList) {
  var locMap = {};
  
  // clone. If you want to directly modify the issues, this line is no need.
  issueList = JSON.parse(JSON.stringify(issueList));
  
  // construct map from location list.
  locationList.forEach(function(location) {
    locMap[location.id] = location;
  });
  
  // Use the map to bind location and issue.
  issueList.forEach(function(issue) {
    var loc = locMap[issue.location_id];
    if (loc) {
      issue.location = loc;
    }
  });
  
  
  // If you don't want to clone, this line is no need.
  return issueList;
};

var bindResult = binds(locations, issues);
console.log(bindResult);

Upvotes: 1

Juned Lanja
Juned Lanja

Reputation: 1474

1. you can optimize your code to :

var locationObj = {};
for(i = 0; i < location.length; i++) {
    locationObj[location[i].id] = location[i];         
}
for(i = 0; i < issues.length; i++) {
    if(issues[i].location_id){
           issues[i].location = locationObj[location_id] 
    }
}

It will cache all location in one object and will directly use that location detail it as that object's property.It will be faster in execution rather than using filter or map on location array each time.

2. if your location's id and location array's index are in sync then following would be a better and faster solution.

for(i = 0; i < issues.length; i++) {
        if(issues[i].location_id){
           issues[i].location = locations[location_id-1] 
        }
    }

Upvotes: 0

potatopeelings
potatopeelings

Reputation: 41075

You can use forEach and filter

var result = issues.forEach(function (issue) {
    issue.location = locations.filter(function (location) {
        return location.id == issue.location_id;
    })[0]
})

which gives you this output when you console.log(result)

[{
    "id": 1,
    "title": "issue 1",
    "location_id": 1,
    "location": {
        "id": 1,
        "name": "location 1"
    }
}, {
    "id": 12,
    "title": "issue 1",
    "location_id": 2,
    "location": {
        "id": 2,
        "name": "location 2"
    }
}]

Upvotes: 0

Related Questions