16

I have an array with several category objects, each of which has an items property containing an array of item objects. I want to map each item in each category to an object[] with objects that have the properties value and label. For some reason, I can't perform the concatenation.

var categories = [{
                name: "category1",
                items: [{
                    itemId: 1,
                    name: "Item1"
                }, {
                    itemId: 2,
                    name: "Item2"
                }]
            }, {
                name: "category2",
                items: [{
                    itemId: 3,
                    name: "Item3"
                }, {
                    itemId: 4,
                    name: "Item4"
                }]
            }];

var items = [];
for(var i = 0; i < categories.length; i++){
    items.concat($.map(categories[i].items,function(elem){
        return {value:elem.itemId, label:elem.name};
    }));
} 
console.log(items); //prints []

Expected Result

[{
   label: "Item1",
   value: "1"
},
{
   label: "Item2",
   value: "2"
},{
   label: "Item3",
   value: "3"
},{
   label: "Item4",
   value: "4"
}

I feel as if I am missing something very basic. I logged the result of the $.map function and it appears to be returning an []. Can anyone figure out the issue?

JSFiddle: http://jsfiddle.net/vymJv/

1
  • Please disregard. I needed to assign items the result of the concat() Commented Jun 12, 2013 at 15:26

6 Answers 6

29

Another method using straight Javascript:

var x = categories.map(function(val) {
  return val.items;
}).reduce(function(pre, cur) {
   return pre.concat(cur);
}).map(function(e,i) {
  return {label:e.name,value:e.itemId};
});

Output: x = [{label: "Item1", value: 1}, {label: "Item2", value: 2}, …]

Sign up to request clarification or add additional context in comments.

1 Comment

Shorter, ES6+ version of this: var x = categories.map(val => val.items).reduce((pre, cur) => pre.concat(cur)).map(e => ({label: e.name, value: e.itemId}));
17

Updated with flatMap (not compatible with IE):

categories.flatMap((categories) => categories.items)

flatMap() method returns a new array formed by applying a given callback function to each element of the array, and then flattening the result by one level.

1 Comment

this did the trick, not sure why this answer is not the top one
10

The concat() method is used to join two or more arrays.

This method does not change the existing arrays, but returns a new array, containing the values of the joined arrays.

http://jsfiddle.net/vymJv/1/

for(var i = 0; i < categories.length; i++){
    items = items.concat($.map(categories[i].items, function(elem) {
        return {value: elem.itemId, label: elem.name};
    }));
}

1 Comment

Thanks for the answer, I realized this shortly after I posted. I guess I needed to lay it out. I will accept in a few.
4

This piece of code solves your task using a functional programming approach:

var items = [].concat.apply([], categories.map(cat =>
  cat.items.map(elem => ({ value:elem.itemId, label:elem.name })))
)

Explanation: Function.prototype.apply() has the syntax fun.apply(thisArg, [argsArray]) and lets us provide parameters to a function in an array. Array.prototype.concat() combines an arbitrary amount of parameters into one array. If we now write Array.prototype.concat.apply([], [category1Items, ..., categoryNItems]), it actually equals [].concat(category1Items, ..., categoryNItems), which concatenates all parameters together. You can also replace Array.prototype.concat by [].concat to keep it shorter. Otherwise we just use standard mapping to get the job done.

You could also split the code apart a bit more for clarity:

function getItem(elem){
  return {value:elem.itemId, label:elem.name};
}

function getCategoryItems(cat) {
  return cat.items.map(getItem);
}

function flatten(arr) {
  return Array.prototype.concat.apply([], arr);
}

var items = flatten(categories.map(getCategoryItems));

1 Comment

Not sure when the syntax was defined, but nowadays [].concat(...arr) should achieve the same as your flatten definition
4
  const items = categories
          .map(category => category.items)
          .reduce((prev, current) => [...prev, ...current])
          .map((e, i) => ({ label: e.name, value: e.itemId }));

1 Comment

Code only answers can almost always be improved by the addition of some explanation of how and why.
3

We could extend the array prototype by creating concatAll which will concatenate all of your arrays by iterating over each subarray, dumping each value into a new array.

Array.prototype.concatAll = function() {
  var results = [];
  this.forEach(function(subArray) {
    subArray.forEach(function(subArrayValue) {
      results.push(subArrayValue);
    });
  });
  return results;
};

Then, we can get the desired result with the following:

let items = categories.map(function(category) {
  return category.items.map(function(item) {
    return {label: item.name, value: item.itemId};
  });
}).concatAll();

We get the items by translating each category into an array of items. Because we have two categories, we will end up with two arrays of items. By applying concatAll on the final result, we flatten those two arrays of items and get the desired output.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.