Trying to get the sum for each Object - javascript

I have been trying to figure this out, but I seem to be going nowhere. Basically I have a JSON that outputs.
[
{
"gosuResponse" : {
"tokenId" : "60e2d532-3d1c-4a95-adbd-aa352984c125",
"page" : 1,
"pageSize" : 1000,
"nbLinesTotal" : 15,
"serials" : {
"serial" : [ "272072207980" ]
},
"data" : {
"row" : [ {
"col" : [ "2015-02-10", "", "1"]
}, {
"col" : [ "2015-02-10", "BNP-Blogs", "1504"]
}, {
"col" : [ "2015-02-10", "BNP", "66"]
}, {
"col" : [ "2015-02-10", "GOOMPlayer-Site", "6"]
}, {
"col" : [ "2015-02-10", "podcast", "19"]
}, {
"col" : [ "2015-02-10", "stream", "10"]
}, {
"col" : [ "2015-02-09", "", "6"]
}, {
"col" : [ "2015-02-09", "BNP-Blogs", "1742"]
}, {
"col" : [ "2015-02-09", "BNP", "61"]
}, {
"col" : [ "2015-02-09", "GOOMPlayer-Site", "2"]
}, {
"col" : [ "2015-02-09", "podcast", "18"]
}, {
"col" : [ "2015-02-09", "stream", "8"]
}, {
"col" : [ "2015-02-08", "", "7"]
}, {
"col" : [ "2015-02-01", "stream", "8"]
} ]
}
}
}
]
Since there are similar names, I grouped them together using underscore.js
var items = result[0].gosuResponse.data.row;
var groups = _(items).groupBy(function(o) {
return o.col[1];
});
console.log(groups);
This outputs,
Object
- BNP : Array[4]
- 0 : Object
- col : Array[3]
0 : '2015-02-10"
1 : 'BNP'
2: '66'
- 1 : Object
- col : Array[3]
0 : '2015-02-10"
1 : 'BNP'
2: '66'
I am trying to add up the number value in position 2 for each Object.
I tested with one key in my Plunkr, but I was wondering if there is a way to do it for all objects?
My Plunkr http://plnkr.co/edit/nNwNoAiUz4PKV8ucaPc1?p=preview

I think there is no reasons to group items:
var sum = {};
_.each(items, function(row) {
var col = row.col;
if (sum.hasOwnProperty(col[1])) {
sum[col[1]] += parseInt(col[2]) || 0;
} else {
sum[col[1]] = parseInt(col[2]) || 0;
}
});
But note I'm relatively new to underscore.js and did not know much about its specific tricks.
Update:
I've found a native underscore.js solution with using groups also:
var groups = _(items).groupBy(function(o) {
return o.col[1];
});
var sum2 = {};
_.each(groups, function(group, key) {
sum2[key] = _.reduce(group, function(memo, item) {
return memo + (parseInt(item.col[2]) || 0);
}, 0);
});

Related

Javascript way of performing basic merge on two object [duplicate]

This question already has answers here:
Merge two javascript objects adding values of common properties
(4 answers)
How to merge objects and sum just some values of duplicate objects? [duplicate]
(1 answer)
Closed 2 years ago.
I have two objects that look like:
{
"data" : [
{
"name" : "toyota",
"type" : "cars",
"totals" : {
"invalid" : 4,
"valid" : 14,
"percentage" : 77.78,
"total" : 18
}
}
],
"report_id": "123wa31a22aba05"
}
I would like to merge those two objects into one object with the following set of rules:
Every two cars that have the same name and type in the data should be merged. This means that totals will be:
"totals": {
"invalid": "invalidA" + "invalidB"
"valid": "validA" + "validB"
"percentage" : calculatePercentage("invalid","valid")
"total": "invalid" + "valid"
}
If there is only sub-object with some name and type, it will just push it as it to the merged report.
What I thought: Copy object one to result object. Then iterate over the second object and insert the elements into the result object (merge if needed). I would use the for loop as I'm used from Java, but it doesn't feel a good js code. What is the proper way to merge those two object in JS?
Example to make it easier:
Object 1:
{
"data" : [
{
"name" : "toyota",
"type" : "cars",
"totals" : {
"invalid" : 4,
"valid" : 14,
"percentage" : 77.78,
"total" : 18
}
}
],
"report_id": "123wa31a22aba05"
}
Object 2:
{
"data" : [
{
"name" : "toyota",
"type" : "cars",
"totals" : {
"invalid" : 2,
"valid" : 5,
"percentage" : 71.42,
"total" : 7
}
}
],
"report_id": "123wa31a22aba06"
}
Result:
{
"data" : [
{
"name" : "toyota",
"type" : "cars",
"totals" : {
"invalid" : 6,
"valid" : 19,
"percentage" : 76.0,
"total" : 25
}
}
]
}
This solution might help you.
var a = {
"data": [
{
"name": "toyota",
"type": "cars",
"totals": {
"invalid": 4,
"valid": 14,
"percentage": 77.78,
"total": 18
}
}
],
"report_id": "123wa31a22aba05"
},
b = {
"data": [
{
"name": "toyota",
"type": "cars",
"totals": {
"invalid": 2,
"valid": 5,
"percentage": 71.42,
"total": 7
}
}
],
"report_id": "123wa31a22aba06"
},
c = [...a.data, ...b.data];
var tmp = [];
for (let i = 0; i < c.length; i++) {
for (let j = i + 1; j < c.length; j++) {
if(c[i].name == c[j].name) {
c[i].totals.invalid += c[j].totals.invalid;
c[i].totals.valid += c[j].totals.valid;
c[i].totals.total += c[j].totals.total;
c[i].totals.percentage = (c[i].totals.valid / c[i].totals.total) * 100;
c.splice(j, 1)
}
}
}
console.log(c);

How to display all fields of a nested json in table format using Bootstrap

I want to write a utility which connects to a REST api downloads data in JSON format and then paints the data as nested tables using Bootstrap.
JSON Data -
[
{
"id" : "Id1",
"name" : "Name1",
"orders" : [{"orderId" : "o1", "size" : 34}, {"orderId" : "o2", "size" : 3}]
},
{
"id" : "Id2",
"name" : "Name2",
"orders" : [
{"orderId" : "o3", "size" : 5, "addresses" : [{"addressId" : "a1", "phone" : "1235"}, {"addressId" : "a2", "phone" : 555}]},
{"orderId" : "o4", "size" : 5, "addresses" : [{"addressId" : "a3", "phone" : "1235"}]}
]
}
]
I looked at the sub-table feature of Bootstrap, however it seems that it would need lot of custom code to get this working. Is there a better way to bind the json to table in a generic way?
Edit
After spending some time I was able to achieve this -
As you can see, I could get one level of nesting, however i just need to go one level deep. Any suggestions?
<script>
var $table = $('#table')
function buildTable($el, jsonData) {
var i; var j; var row
var columns = []
var data = []
if(!Array.isArray(jsonData) && jsonData.length == 0) {
return;
}
Object.keys(jsonData[0]).forEach( (k) => {
columns.push({
field: k,
title: k,
sortable: true
})
})
for(var j = 0; j < jsonData.length; j++) {
row = {}
Object.keys(jsonData[j]).forEach( (k) => {
row[k] = jsonData[j][k]
})
data.push(row)
}
$el.bootstrapTable({
columns: columns,
data: data,
detailFilter: function (index, row) {
console.log("detail filter " + Object.values(row))
for(var k in row) {
if(Array.isArray(row[k])){
return true;
}
}
return false;
},
onExpandRow: function (index, row, $detail) {
console.log("expand row keys " + Object.keys(row))
console.log("expand row vals " + Object.values(row))
var newRow = {};
for(var k in row) {
if(Array.isArray(row[k])){
alert('found ' + row[k])
newRow = row[k]
break
}
}
buildTable($detail.html('<table></table>').find('table'), newRow)
}
})
};
var mydata =
[
{
"id": 0,
"name": "test0",
"price": "$0",
"orders" :
[
{
"name" : "ABC",
"size" : 25,
"someList": [{"a":1, "b":2}, {"a":3, "b":4}]
},
{
"name" : "XYZ",
"size" : 50
}
]
}
/* {
"id": 1,
"name": "test1",
"price": "$1"
},
{
"id": 2,
"name": "test2",
"price": "$2",
"orders" : [{"name" : "def", "size": 45}]
}*/
];
$(function() {
buildTable($table, mydata)
})

How can i get pair of the json object in javascript

data =
{"user" : [
{
"id" : 1,
"name" : "jeboo",
"level": 1
},
{
"id" : 2,
"name" : "yoyo",
"level": 1
},
{
"id" : 3,
"name" : "yaya",
"level": 2
},
{
"id" : 4,
"name" : "yeye",
"level": 2
},
{
"id" : 5,
"name" : "yiyi",
"level": 3
},
{
"id" : 6,
"name" : "jebee",
"level": 3
}
]}
this is how i get json object
var obj = JSON.stringify(data);
var parse = JSON.parse(obj);
$.each(parse, function(key, object) {
$.each(object, function(index, val) {
console.log(index, val);
});
});
my purpose is want to produce the array below
object 0 + object 1 = first pair
object 2 + object 3 = second pair
object 4 + object 5 = third pair
Assuming you want objects with same level in pairs,
You can use Array#reduce to achieve this:
var data =
{"user" : [
{
"id" : 1,
"name" : "jeboo",
"level": 1
},
{
"id" : 2,
"name" : "yoyo",
"level": 1
},
{
"id" : 3,
"name" : "yaya",
"level": 2
},
{
"id" : 4,
"name" : "yeye",
"level": 2
},
{
"id" : 5,
"name" : "yiyi",
"level": 3
},
{
"id" : 6,
"name" : "jebee",
"level": 3
}
]};
var r = data.user.reduce(function(res, obj) {
res[obj.level - 1] = res[obj.level - 1] || [];
res[obj.level - 1].push(obj);
return res;
}, []);
console.log(r);
EDIT
If you want to pair the objects according to their position in the array and independent of level property then you can use callback's index argument and a bit of math:
var r = data.user.reduce(function(res, obj, idx) {
res[Math.floor(idx/2)] = res[Math.floor(idx/2)] || [];
res[Math.floor(idx/2)].push(obj);
return res;
}, []);

Group nested array in Javascript

I have an array of data that is being used for some visualisation and is in the below format
var Dataset1 = [
{
"commentBy" : "saurabh",
"comment" : "Testing",
"datestamp" : "07/07/2017",
"weekcount" : 1
},
{
"commentBy" : "raman",
"comment" : "Planning",
"datestamp" : "07/07/2017",
"weekcount" : 1
},
{
"commentBy" : "Execution",
"comment" : "Alfa Beta",
"datestamp" : "07/07/2017",
"weekcount" : 2
},
{
"commentBy" : "Execution",
"comment" : "Zseta Gama",
"datestamp" : "07/07/2017",
"weekcount" : 2
}
]
//although i have tried writing this function but this is not giving me the desired result.
var groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
(rv[x[key]] = rv[x[key]] || []).push(x);
return rv;
}, {});
};
var groubedByTeam=groupBy(Dataset1, 'weekcount')
console.log(groubedByTeam);
I want to grouped the dataset by the weekcount so that the desired result should be like this.
[
{ "weekcount" : 1
"grouped" : [
{ "commentBy" : "saurabh",
"comment" : "Testing",
"datestamp" : "07/07/2017"
},
{
"commentBy" : "raman",
"comment" : "Planning",
"datestamp" : "07/07/2017"
}
]
}, {
"weekcount" : 2
"grouped" : [
{
"commentBy" : "Execution",
"comment" : "Alfa Beta",
"datestamp" : "07/07/2017",
},
{
"commentBy" : "Execution",
"comment" : "Zseta Gama",
"datestamp" : "07/07/2017",
}
]
}
]
const formatted = [];
Dataset1.forEach((data) => {
const { weekcount, comment, commentBy, datestamp } = data;
let obj = formatted.find((item) => item.weekcount === weekcount);
if (!obj) {
formatted.push({
weekcount,
grouped: [{
comment,
commentBy,
datestamp
}]
})
} else {
obj.grouped.push({
comment,
commentBy,
datestamp
});
}
});
const Dataset1 = [{
"commentBy": "saurabh",
"comment": "Testing",
"datestamp": "07/07/2017",
"weekcount": 1
}, {
"commentBy": "raman",
"comment": "Planning",
"datestamp": "07/07/2017",
"weekcount": 1
}, {
"commentBy": "Execution",
"comment": "Alfa Beta",
"datestamp": "07/07/2017",
"weekcount": 2
}, {
"commentBy": "Execution",
"comment": "Zseta Gama",
"datestamp": "07/07/2017",
"weekcount": 2
}];
const formatted = [];
Dataset1.forEach((data) => {
const { weekcount, comment, commentBy, datestamp } = data;
let obj = formatted.find((item) => item.weekcount === weekcount);
if (!obj) {
formatted.push({
weekcount,
grouped: [{
comment,
commentBy,
datestamp
}]
})
} else {
obj.grouped.push({
comment,
commentBy,
datestamp
});
}
});
console.log(formatted);
Here is a clean way to group the data, you should be able to figure out how to format it the way you want with this as a starting point.
grouped = {}
Dataset1.forEach(function(item, index){
if (!grouped[item.weekcount]) grouped[item.weekcount] = [];
grouped[item.weekcount].push(item);
});
grouped is an object keyed with the weekcount. If a certain weekcount doesn't exist as a key in the object, an empty array is created and then the data is pushed to it. On later iterations data with the same weekcount is added to the existing array.
You could check each weekcount from 0 to max, and filter your array. It could be something like this:
var Dataset1 = [
{
"commentBy" : "saurabh",
"comment" : "Testing",
"datestamp" : "07/07/2017",
"weekcount" : 1
},
{
"commentBy" : "raman",
"comment" : "Planning",
"datestamp" : "07/07/2017",
"weekcount" : 1
},
{
"commentBy" : "Execution",
"comment" : "Alfa Beta",
"datestamp" : "07/07/2017",
"weekcount" : 2
},
{
"commentBy" : "Execution",
"comment" : "Zseta Gama",
"datestamp" : "07/07/2017",
"weekcount" : 2
}
]
var maxWeekCount = 3;
var result = []
for(var i=0; i<maxWeekCount; i++){
var group = Dataset1.filter(obj => obj.weekcount === i)
if(group.length) {
result.push({
weekCount: i,
grouped: group
})
}
}
console.log(result)
Use a helper object, that maintains a reference to the weekcount objects, to reduce the array to the grouped structure.
var Dataset1 = [{"commentBy":"saurabh","comment":"Testing","datestamp":"07/07/2017","weekcount":1},{"commentBy":"raman","comment":"Planning","datestamp":"07/07/2017","weekcount":1},{"commentBy":"Execution","comment":"Alfa Beta","datestamp":"07/07/2017","weekcount":2},{"commentBy":"Execution","comment":"Zseta Gama","datestamp":"07/07/2017","weekcount":2}];
var helperMap = {};
var result = Dataset1.reduce(function(arr, obj) {
var current = helperMap[obj.weekcount];
if(!current) {
current = {
weekcount: obj.weekcount,
grouped: []
};
helperMap[obj.weekcount] = current;
arr.push(current);
}
current.grouped.push({
commentBy: obj.commentBy,
comment: obj.comment,
datestamp: obj.datestamp
});
return arr;
}, []);
console.log(result);
var groupBy = function(xs, key) {
return xs.reduce(function(rv, x) {
if(rv[x[key]] == undefined){
rv[x[key]] = {"weekcount": x[key], "grouped": []}
}
stripped = {}
for(var k in x) if(k!=key) stripped[k]=x[k]; //strip "key" property
rv[x[key]]["grouped"].push(stripped);
return rv;
}, []);
};
By stripping the "key" property, this solution works with any input without modification, so if you add/remove some properties from the input, it will still work as expected, reflecting the changes.

How to return sum of unknown fields' name?

I have data like this, and want to calculate how many i have true/false values for all fields
{
"key1" : true,
"key2" : false,
"key3" : true
},
{
"key1" : false,
"key2" : true,
"key3" : true
}
Expected result is
{
key1: { true: 1, false: 1 },
key2: { true: 1, false: 1 },
key3: { true: 2, false: 0 }
}
I can calc it for specific field with group by operator, but i don't know how to do this for all fields in collection
Closest you can get is this:
db.foo.aggregate([{
$group: {
_id: null,
key1 : {
$sum: {
$cond: {
if: "$key1",
then: 1,
else: 0
}
}
},
key2 : {
$sum: {
$cond: {
if: "$key2",
then: 1,
else: 0
}
}
},
key3 : {
$sum: {
$cond: {
if: "$key3",
then: 1,
else: 0
}
}
}
}
}])
Which will give you:
{
"_id" : null,
"key1" : 1,
"key2" : 1,
"key3" : 2
}
If you have no idea of the number of keys' name in you document, then you can't use the aggregation framework, instead what you need here is mapReduce and output the map reduce result into a new collection using the out option or display the result in the shell using inline: 1. Here we the option is used because we need extra processing step in order to get the expect result.
db.collection.mapReduce(
function() {
var keys = Object.keys(this);
for(var ind=0; ind<keys.length; ind++) {
if (keys[ind] !== "_id") {
var d = {};
d.name = keys[ind],
d.value= this[keys[ind]];
emit(d, 1);
}
}
},
function(key, values) { return Array.sum(values); },
{ "out": "mresult" }
)
which returns something like this:
{
"result" : "mresult",
"timeMillis" : 566,
"counts" : {
"input" : 2,
"emit" : 6,
"reduce" : 1,
"output" : 5
},
"ok" : 1
}
Five documents where saved in the newly created collection as shown by the mapReduce output. You can easily very this using the .find()
db.mresult.find()
which yields something like this:
{ "_id" : { "name" : "key1", "value" : false }, "value" : 1 }
{ "_id" : { "name" : "key1", "value" : true }, "value" : 1 }
{ "_id" : { "name" : "key2", "value" : false }, "value" : 1 }
{ "_id" : { "name" : "key2", "value" : true }, "value" : 1 }
{ "_id" : { "name" : "key3", "value" : true }, "value" : 2 }
As you can see even using mapReduce we can't get the expected result which is somehow a bit annoying, but these documents can easily be processed using the .aggregate method.
The first stage in you pipeline is the $project stage where you basically use the $cond conditional operator to "set" the value of "true" and "false". The last stage in the pipeline is the $group stage where you group your documents and use the $sum accumulator operator to return the sum for each group.
db.mresult.aggregate([
{ "$project": {
"true": {
"$cond": [
{ "$eq": [ "$_id.value", true ] },
"$value",
0
]
},
"false": {
"$cond": [
{ "$eq": [ "$_id.value", false ] },
"$value",
0
]
}
}},
{ "$group": {
"_id": "$_id.name",
"true": { "$sum": "$true" },
"false": { "$sum": "$false" }
}}
])
which produces something like this:
{ "_id" : "key3", "true" : 2, "false" : 0 }
{ "_id" : "key2", "true" : 1, "false" : 1 }
{ "_id" : "key1", "true" : 1, "false" : 1 }
Of course this is not exactly you expected output but much more better because, generally speaking using data as key is not a good idea.
I would go with callback passed to find query. it will give you more flexibility.
Sample.find({}, function (err, docs) {
if (!err) {
var result = {};
for (var i = 0; i < docs.length; i++) {
var currDoc = docs[i]._doc;
delete currDoc._id;
Object.keys(currDoc).forEach(function (key) {
var processedKey = result[key] ? result[key] : {"false": 0, "true": 0};
var count = (processedKey["" + currDoc[key]] | 0 ) + 1;
processedKey["" + currDoc[key]] = count;
result[key] = processedKey;
});
}
console.log(result);
process.exit();
} else {
throw err;
}
});
for input
[
{
"key1": true,
"key2": false,
"key3": true
},
{
"key1": true,
"key2": false,
"key3": true
}
]
It will output
{
key3: {
false: 0,
true: 2
},
key2: {
false: 2,
true: 0
},
key1: {
false: 0,
true: 2
}
}

Categories

Resources