phileras phileras - 5 months ago 19
Javascript Question

How to avoid overwrite strings while reducing an array?

I have an array grouped by different categories and each category have nested more objects with different values (numeric and strings) which i have to reduce (except the strings). Reduce is working fine with numeric values making a sum of them but its overwriting the string values keeping just the last one.

I'm trying to reduce an object/dictionnary by category and fill a table with the sum of the clicks of each category but don't sum them if the subcategories and names are different.

Here is a demo:



var data = {
'Category xxxx': [
{
units: 1234,
subcategory: 'wolves',
name: 'Starks'
},
{
units: 1345354,
subcategory: 'wolves',
name: 'Starks'
},
{
units: 666,
subcategory: 'dragons',
name: 'Targaryens'
}
],
'Category yyyy': [
{
units: 7783,
subcategory: 'lions',
name: 'Lanisters'
},
{
units: 1267878,
subcategory: 'spires',
name: 'Martells'
}
]
}

var test = _.map(data, function (value, key) {
var returnedData = {
Category: key,
units: _(value).reduce(function (memo, metrics) {
return memo + metrics.units;
}, 0),
subcategory: _(value).reduce(function (memo, metrics) {
return metrics.subcategory;
}, 0),
name: _(value).reduce(function (memo, metrics) {
return metrics.name;
}, 0),
};
return returnedData;
});

<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>





The clicks are adding fine but the strings get overwritten by the last iterated string property.

I want to obtain something like this where if the strings are different the integers are not added up.

returnedData = {
'Category xxxx': [
{
units: 1346588,
chivalry: 'wolves',
name: 'Starks'
},
{
units: 666,
subcategory: 'dragons',
name: 'Targaryens'
}
],
'Category yyyy': [
{
clicks: 7783,
subcategory: 'lions',
name: 'Lanisters'
},
{
clicks: 1267878,
subcategory: 'spires',
name: 'Martells'
}
]
}


What is the best way to do it?

Answer

Finally, this proposal uses an object as reference to the items of the result object.

If no result object is available, then it creates a new one with the wanted category. For subcategories it generates an element which is pushed to the result array.

The count is maintained with hash as reference to the elements of result.

var data = { 'Category xxxx': [{ clicks: 1234, subcategory: 'dogs', name: 'jhon doe' }, { clicks: 1345354, subcategory: 'dogs', name: 'jhon doe' }], 'Category yyyy': [{ clicks: 7783, subcategory: 'frogs', name: 'lanisters' }, { clicks: 1267878, subcategory: 'rats', name: 'perry' }] },
    result = {};

Object.keys(data).forEach(function (k) {
    var hash = Object.create(null);
    if (!result[k]) {
        result[k] = [];
    }
    data[k].forEach(function (a) {
        if (!hash[a.subcategory]) {
            hash[a.subcategory] = { clicks: 0, subcategory: a.subcategory, name: a.name };
            result[k].push(hash[a.subcategory]);
        }
        hash[a.subcategory].clicks += a.clicks;
    });
});

console.log(result);