web-dev-qa-db-fra.com

JavaScript SUM et GROUP BY de données JSON

C’est la première fois que je tente d’utiliser JavaScript avec des objets de données JSON et j’ai besoin de conseils sur la bonne façon d’atteindre mon objectif.

Certains codes côté serveur génèrent en fait une chaîne au format JSON avec laquelle je dois travailler et l’affecter à une chaîne:

var dataString='$DATASTRING$';

Mais le résultat final avec lequel je dois travailler une fois que le serveur a substitué ses données (sans le\r\n, bien sûr):

var dataString='[ 
 { "category" : "Search Engines", "hits" : 5, "bytes" : 50189 },
 { "category" : "Content Server", "hits" : 1, "bytes" : 17308 },
 { "category" : "Content Server", "hits" : 1, "bytes" : 47412 },
 { "category" : "Search Engines", "hits" : 1, "bytes" : 7601 },
 { "category" : "Business", "hits" : 1, "bytes" : 2847 },
 { "category" : "Content Server", "hits" : 1, "bytes" : 24210 },
 { "category" : "Internet Services", "hits" : 1, "bytes" : 3690 },
 { "category" : "Search Engines", "hits" : 6, "bytes" : 613036 },
 { "category" : "Search Engines", "hits" : 1, "bytes" : 2858 } 
]';

Et puis je peux le changer pour un objet avec lequel travailler.

var dataObject=eval("("+dataString+")");

Cela me permet d’accéder aux différentes lignes de données, mais je dois additionner, grouper par et classer les valeurs.

J'ai besoin de l'équivalent d'une instruction SQL comme ceci:

SELECT category, sum(hits), sum(bytes) 
FROM dataObject
GROUP BY category
ORDER BY sum(bytes) DESC

Ma sortie désirée serait un objet comme celui-ci que je pourrais traiter ultérieurement:

var aggregatedObject='[ 
 { "category" : "Search Engines", "hits" : 13, "bytes" : 673684 },
 { "category" : "Content Server", "hits" : 3, "bytes" : 88930 },
 { "category" : "Internet Services", "hits" : 1, "bytes" : 3690 },
 { "category" : "Business", "hits" : 1, "bytes" : 2847 } 
]';

... mais je ne sais pas par où commencer.

Je pourrais parcourir toutes les valeurs de catégorie et trouver les catégories uniques en premier, puis relancer et additionner les occurrences et les octets, puis à nouveau pour trier, mais il semble qu'il doit y avoir un moyen plus facile.

prototype.js (1.7) est déjà inclus sur la page client, mais je pourrais ajouter Underscore, jQuery ou une autre petite bibliothèque si nécessaire.

Je ne sais pas ce qui serait le mieux, le plus facile, le plus petit avec le moins de code possible pour traiter la requête.

Aucune suggestion?

20
LordChariot

Vous pouvez utiliser les fonctions natives .reduce() pour agréger les données, puis .sort() pour les trier par bytes.

var result = dataObject.reduce(function(res, obj) {
    if (!(obj.category in res))
        res.__array.Push(res[obj.category] = obj);
    else {
        res[obj.category].hits += obj.hits;
        res[obj.category].bytes += obj.bytes;
    }
    return res;
}, {__array:[]}).__array
                .sort(function(a,b) { return b.bytes - a.bytes; });

Si vous supportez des implémentations plus anciennes, vous devrez utiliser un shim pour .reduce() .

24
user1106925

Si vous choisissez la route LINQ.js , vous pouvez le faire comme ceci:

var aggregatedObject = Enumerable.From(dataArray)
        .GroupBy("$.category", null,
                 function (key, g) {
                     return {
                       category: key,
                       hits: g.Sum("$.hits"),
                       bytes: g.Sum("$.bytes")
                     }
        })
        .ToArray();

Démo de travail avec Stack Snippets:

var dataArray = [ 
  { category: "Search Engines", hits: 5, bytes: 50189 },
  { category: "Content Server", hits: 1, bytes: 17308 },
  { category: "Content Server", hits: 1, bytes: 47412 },
  { category: "Search Engines", hits: 1, bytes: 7601  },
  { category: "Business",       hits: 1, bytes: 2847  },
  { category: "Content Server", hits: 1, bytes: 24210 },
  { category: "Internet ",      hits: 1, bytes: 3690  },
  { category: "Search Engines", hits: 6, bytes: 613036 },
  { category: "Search Engines", hits: 1, bytes: 2858  } 
];

var aggregatedObject = Enumerable.From(dataArray)
        .GroupBy("$.category", null,
                 function (key, g) {
                     return {
                       category: key,
                       hits: g.Sum("$.hits"),
                       bytes: g.Sum("$.bytes")
                     }
        })
        .ToArray();

console.log(aggregatedObject);
<script src="//cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.min.js"></script>

En outre, vous pouvez trouver plus d’informations sur le groupe linqjs avec une somme

8
KyleMit

Bonjour, voici une solution écrite par moi Visitez: aggreg_groupby_js sur npm Ou dans aggreg_groupby_js sur github

La bibliothèque javascript permettant d’utiliser des fonctions d’agrégation sur un tableau d’objets. Fonctions de base comme SUM, MIN, MAX, AVG, DISTINCT_COUNT pour des objets javascript entiers

Exemple:

var arr = [{`"shape"`:`"square"`,`"color"`:`"red"`,`"used"`:1,`"instances"`:1},
    {`"shape"`:`"square"`,`"color"`:`"red"`,`"used"`:2,`"instances"`:1},
    {`"shape"`:`"circle"`,`"color"`:`"blue"`,`"used"`:0,`"instances"`:0},
    {`"shape"`:`"square"`,`"color"`:`"blue"`,`"used"`:4,`"instances"`:4},
    {`"shape"`:`"circle"`,`"color"`:`"red"`,"`used"`:1,`"instances"`:1},
    {`"shape"`:`"circle"`,`"color"`:`"red"`,`"used"`:1,`"instances"`:0},
    {`"shape"`:`"square"`,`"color"`:`"blue"`,`"used"`:4,`"instances"`:5}, 
    {`"shape"`:`"square"`,`"color"`:`"red"`,`"used"`:2,`"instances"`:1}];

// Specify columns
    var columns =[`"used"`, `"instances"`];

// Initialize object
    var gb = new GroupBy(arr,columns);
// or
    var gb = new GroupBy(arr,[`"used"`, `"instances"`]);

// Call the aggregate functions    
    gb.sum();
    gb.min();
    gb.max();
    gb.avg();
    gb.distinctCount();
1
Naim Sulejmani
var obj = [{Poz:'F1',Cap:10},{Poz:'F1',Cap:5},{Poz:'F1',Cap:5},{Poz:'F2',Cap:20},{Poz:'F1',Cap:5},{Poz:'F1',Cap:15},{Poz:'F2',Cap:5},{Poz:'F3',Cap:5},{Poz:'F4',Cap:5},{Poz:'F1',Cap:5}];
Array.prototype.sumUnic = function(name, sumName){
    var returnArr = [];
    var obj = this;
    for(var x = 0; x<obj.length; x++){
        if((function(source){
            if(returnArr.length == 0){
                return true;
            }else{
                for(var y = 0; y<returnArr.length; y++){
                    var isThere = [];
                    if(returnArr[y][name] == source[name]){
                        returnArr[y][sumName] = parseInt(returnArr[y][sumName]) + parseInt(source[sumName]);
                        return false;
                    }else{
                        isThere.Push(source);
                    }
                }
                if(isThere.length>0)returnArr.Push(source);
                return false;
            }
        })(obj[x])){
            returnArr.Push(obj[x]);
        }
    }
    return returnArr;
}
obj.sumUnic('Poz','Cap');
// return "[{"Poz":"F1","Cap":45},{"Poz":"F2","Cap":25},{"Poz":"F3","Cap":5},{"Poz":"F4","Cap":5}]"
1
AhbapAldirmaz

Compte tenu de la chaîne de données ci-dessus, le code ci-dessous semble fonctionner. Il traverse chaque objet; si la catégorie existe dans le tableau groupedObjects, ses occurrences et ses octets sont ajoutés à l'objet existant. Sinon, il est considéré comme nouveau et ajouté au tableau groupedObjects.

Cette solution utilise underscore.js et jQuery

Voici une démo de jsfiddle: http://jsfiddle.net/R3p4c/2/

var objects = $.parseJSON(dataString);
var categories = new Array();
var groupedObjects = new Array();
var i = 0;

_.each(objects,function(obj){
    var existingObj;
    if($.inArray(obj.category,categories) >= 0) {
        existingObj = _.find(objects,function(o){return o.category === obj.category; });
        existingObj.hits += obj.hits;
        existingObj.bytes += obj.bytes;
    } else {
        groupedObjects[i] = obj;
        categories[i] = obj.category;
        i++;
  }
});

groupedObjects = _.sortBy(groupedObjects,function(obj){ return obj.bytes; }).reverse();
1
jackwanders