How to aggregate properties of objects?

If I have an object like this (or similar):

sales = { 
    obs1:{
        Sales1:{
            Region:"North", Value: 200}, 
        Sales2:{
            Region:"South", Value:100}}, 
    obs2:{
        Sales1:{
            Region:"North", Value: 50}, 
        Sales2:{
            Region:"South", Value:20}
    }
}

How can I sum the sum of properties Valueon Region? Answers can be in pure JavaScript or in a library.

The end result should be something similar to this:

totals = {North: 250, South:120}
+4
source share
5 answers

As others have noted, there are no built-in JavaScript functions for this (there are several high-order functions, such as map, but not enough for the task). However, some libraries, such as Underscore.js , provide many utilities to simplify these kinds of tasks.

var totals = _
    .chain(sales) // Wraps up the object in an "underscore object",
                  // so methods can be chained
    // First: "flatten" the sales
    .map(function(v) { 
        return _
            .chain(v)
            .map(function(v2) {
                return v2;
            })
            .value(); 
    })
    .flatten()
    // Second: group the sales by region
    .groupBy('Region')
    // Third: sum the groups and create the object with the totals
    .map(function(g, key) {
        return {
            type: key, 
            val: _(g).reduce(function(m, x) {
                return m + x.Value;
            }, 0)
        };
    })
    .value(); // Unwraps the "underscore object" back to a plain JS object

Source: this answer in SOpt

, - , . , Region Value - , , - , , - , , .

function aggregate(object, toGroup, toAggregate, fn, val0) {
    function deepFlatten(x) {
        if ( x[toGroup] !== undefined ) // Leaf
            return x;
        return _.chain(x)
                .map(function(v) { return deepFlatten(v); })
                .flatten()
                .value();
    }

    return _.chain(deepFlatten(object))
            .groupBy(toGroup)
            .map(function(g, key) {
                return {
                    type: key,
                    val: _(g).reduce(function(m, x) {
                        return fn(m, x[toAggregate]);
                    }, val0 || 0)
                };
            })
            .value();
}

:

function add(a,b) { return a + b; }
var totals = aggregate(sales, "Region", "Value", add);

( ):

function min(a,b) { return a < b ? a : b; }
var mins = aggregate(sales, "Region", "Value", min, 999999);
+6

, () http://jsfiddle.net/tjX8p/2/ , @MattBurland, , , , .

/**
 * @param {object} obj Arbitrarily nested object that must contain the 
 * given propName and groupPropName at some level
 * @param {string} propName The property to be summed up
 * @param {string} groupPropName The property to group by
 * @param {object} This is used internally for recursion, feel free to pass 
 * in an object with existing totals, but a default one is provided
 * @return {object} An object keyed by by the groupPropName where the value 
 * is the sum of all properties with the propName for that group
 */
function sumUp(obj, propName, groupPropName, totals) {
    var totals = totals || {};
    for (var prop in obj) {
        if (prop === propName) {
            if (!totals[obj[groupPropName]]) {
                totals[obj[groupPropName]] = 0
            } 
            totals[obj[groupPropName]] += obj[propName]
        } else if (typeof obj[prop] == 'object'){
            sumUp(obj[prop], propName, groupPropName, totals);
        }
    }
    return totals;
}

, , -

var sales2 = { 
    a: {
        obs1:{
          Sales1:{
            Region:"North", Value: 100, OtherValue: 12}, 
          Sales2:{
              Region:"South", Value:50, OtherValue: 15}}}, 
    b: {
        obs2:{
          Sales1:{
            Region:"North", Value: 50, OtherValue: 18}, 
          Sales2:{
            Region:"South", Value:100, OtherValue: 20}}
    }
};

console.log (sumUp(sales2, 'Value', 'Region'));
// Object {North: 150, South: 150}
console.log (sumUp(sales2, 'OtherValue', 'Value'));
// Object {50: 33, 100: 32} 

, .

+4

- ( @Barmar):

var totals = {};

function ExtractSales(obj) {
    if (obj.Region && obj.Value) {
        if (totals[obj.Region]) {
            totals[obj.Region] += obj.Value;
        } else {
            totals[obj.Region] = obj.Value;
        }
    } else {
        for (var p in obj) {
            ExtractSales(obj[p]);
        }
    }
}

ExtractSales(sales);

console.log(totals);

http://jsfiddle.net/punF8/3/

, - Region Value. , .

. , , , , , Region Value.

(hasOwnProperty, undefined, ..), .

+3

JS @mgibsonbr, , - jFunk ( jFunk ) Underscore .

totals= _.chain(jF("*[Region]", sales).get()).groupBy('Region').map(function(g, key) {
        return {
            type: key, 
            val: _(g).reduce(function(m, x) {
                return m + x.Value;
            }, 0)
        };
    })
    .value();
+1

, . -, OP, .

, ...

aggPropByAssoc() , property, key/value. , , .

The function makes no assumptions about at what level in the object the requested property can be found. Thus, the function will recursively pass through the object until a property (and the property associated with it) is found.


Syntax

aggPropByAssoc (obj, ops [, cb])

  • object to parse
  • object containing ..,
    • assocKey: key name of the associated property
    • assocVal: string or array of assocKey value
    • property: aggregate property
  • callback function [optional]

Examples

Using the OP example:

// I've removed the object definition for brevity.
var sales = { ... };

aggPropByAssoc( /* obj, ops, cb */
    sales,
    {
        assocKey: 'Region',
        assocVal: ['North', 'South'],
        property: 'Value'
    },
    function (e) {
        // As the function only returns the data found, and does not
        // automatically manipulate it, we use the callback function
        // to return the sum of each region, as requested. 
        return {
            North: e.North.reduce(function (p, c) { return p + c}, 0),
            South: e.South.reduce(function (p, c) { return p + c}, 0)
        }
    }
)

// var regionSums = aggPropByAssoc( ... );
// returns --> Object {North: 250, South: 120}

Source

function aggPropByAssoc(obj, ops, cb) {
    if (typeof obj !== "object" || typeof ops !== "object") { return; }
    if (!(ops.assocKey && ops.assocVal && ops.property)) { return; }

    function recurseObj(robj) {
        for (var k in robj) {
            if (! (robj[k][ops.assocKey] && robj[k][ops.property])) { recurseObj(robj[k]); continue; }
            if (robj[k][ops.assocKey] === ops.assocVal) { aggArr.push(robj[k][ops.property]); }
        }
    }

    var assocVObj = ops.assocVal, aggArr = [], aggObj = {};
    if (typeof ops.assocVal !== "object" ) { 
        recurseObj(obj), aggObj = aggArr;
    } else {
        for (var op in assocVObj) { 
            ops.assocVal = assocVObj[op];
            recurseObj(obj);
            aggObj[ops.assocVal] = aggArr, aggArr = [];
        }
    }

    if (typeof cb === "function") { return cb(aggObj); }
    return aggObj;
}
0
source

Source: https://habr.com/ru/post/1539601/


All Articles