# Building objects in javascript, without “if(!a[k]) a[k] = []”

When building objects using reduce, I often have crappy code like this:

function mapArticlesByTagAndId(articles) {
return articles.reduce((collector = {}, article) => {
const { tag, id } = article;
if(!collector[tag]) collector[tag] = {};

collector[tag][id] = article;
return collector;
})
}


I would love to one line this, but I can't figure out a way to do it. If only assigning a property to undefined created an object =P [Obviously this would cause different problems haha]

• could you please post the complete code? this one is missing the second parameter of the reduce(). – Igor Soloydenko Sep 8 '17 at 16:17
• More specifically "code like this" is not good enough for Code Review. Please post real code that accomplishes a stated task, as per the requirements in the help center and How to Ask. – 200_success Sep 8 '17 at 16:58
• 200, this is real code. The context could be literally anything and it wouldn't change how one would structure this code. I could rename myArr to articles, then as far as anyone knows it's real code. The only difference in context would be performance, which I'm not picky about. – Jason McCarrell Sep 11 '17 at 15:54
• As @IgorSoloydenko says, at least provide a full function call. – 200_success Sep 11 '17 at 16:22

Here's one variant which gets rid of the explicit if at the cost of potential useless reassignment. This, however, does not take away the necessity of ensuring that the k key has a collection object in place...

const result =
myArr.reduce((collector, { k, j, v }) => {
collector[k] = collector[k] || [];
collector[k][j] = v;
return collector;
}, {});


Assuming, ES6 syntax is fine with you, here's another variant. Please notice that it the spread operator (... will result in copying the ...collector[k] item-by-item). I.e. this is less performant and may be an issue for large objects.

const result =
myArr.reduce((collector, { k, j, v }) => {
collector[k] = collector[k] ? [...collector[k], v] : [ v ];
return collector;
}, {});


which may be compacted further:

const result =
myArr.reduce((collector, { k, j, v }) =>
(collector[k] = collector[k] ? [...collector[k], v] : [ v ], collector),
{});


## Update 1

const result =
myArr.reduce((collector, { k, j, v }) =>
(collector[k] = !collector[k] ? [v] : (collector[k].push(v), collector[k])),
{});

• Thanks for all the options! These are options I already knew of, although truth be told I think was intrinsically avoiding the third one, because of all the newly created objects, even though it would technically create a one line reduce function, which is what I was ideally looking for. If there was a clean way to do the last option, without create a new object during each iteration, that would be ideal, however I worry that may not be possible. – Jason McCarrell Sep 11 '17 at 15:52
• @user1272 I'll take a look into it more, at the moment still unsure whether I'll be able to improve it our not. – Igor Soloydenko Sep 11 '17 at 15:54
• @JasonMcCarrell what do you think about the updated version? I reversed the condition to be !collector[k] for readability. (collector[k].push(v), collector[k]) will push a new value into the array and return the array itself, the JS's comma operator is handy here: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… We can not omit the ,collector[k] part because the previous expression (.push()) returns the length of the array after the insertion, rather than the array itself – Igor Soloydenko Sep 11 '17 at 19:14