Group nested array in Javascript - 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.

Related

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)
})

javascript create nested JSON array from array

I have this array :
myArray = [ "Id = 1", "Time = 18:40", "Topic = yyyyyyyyyyyy", "GUEST", "Role = HS", "Infos = Arizona", "Role = GS", "Infos = Arizona", "Role = GS", "Infos = Colorado", "Id = 2","Time = 11:32", "Topic = xoxo", "GUEST", "Role" = "GS", "Infos = California", "Role = CS", "Infos = Maryland", "Role = GS","Infos = Nevada" ];
I want to create a nested JSON array from it:
myJson = [
{
"Id" : "1",
"Time" : "18:40",
"Topic" : "yyyyyyyyyyyy",
"GUEST":
[
{"Role" : "HS",
"Infos" : "Arizona"},
{"Role" : "GS",
"Infos" : "Arizona"},
{"Role" : "HS",
"Infos" : "Colorado"}
]
},
{
"Id" : "2",
"Time" : "11:32",
"Topic" : "xoxo",
"GUEST":
[
{"Role" : "GS",
"Infos" : "California"},
{"Role" : "CS",
"Infos" : "Maryland"},
{"Role" : "GS",
"Infos" : "Nevada"}
]
}
]
How can I do it? tried the code below without success.
myArray = ["Id = 1","Time = 18:40","Topic = yyyyyyyyyyyy","GUEST","Role = HS","Infos = Arizona","Role = GS","Infos = Arizona","Role = GS","Infos = Colorado","Id = 2","Time = 11:32","Topic = xoxo","GUEST","Role" = "GS","Infos = California","Role = CS","Infos = Maryland","Role = GS","Infos = Nevada"];
// Declaring new object
let obj = {};
// Place to remember current level of object
let level;
myJson = [];
for (let item of this.myArray) {
// If it contains an equals
if (item.includes('=')) {
// Split it into two stings
let split = item.split('=');
let key = split[0].trim();
let val = split[1].trim();
// If we're already on a lower level like GUEST3 put it in there
if (level) {
obj[level][key] = val
} else {
// Or just place the new data at the top level
obj[key] = val
}
} else {
// If there's no equals we want to go down a layer
level = item;
myJson.push(obj[item]);
}
}
console.log("myJson : "+ JSON.stringify(myJson));
Another answer is totally right - format you are requesting is totally wrong. If it would have been just going one object deeper when you meet "GUEST" string and create new object in the output when you meet next "Id = \d+" string, you could use something like:
let process = (arr) => {
let path = [];
const data = [];
let o = data;
while (arr && arr.length) {
let item = arr.shift();
let key= null;
let value = null;
if (/Id = \d+/.test(item)) {
o = {};
data.push(o);
let pair = item.split(' = ');
o.Id = pair[1];
}
else if (item == "GUEST") {
o["GUEST"] = {};
o = o["GUEST"]
value = {};
} else {
let pair = item.split(' = ');
o[pair[0]] = pair[1];
}
}
return data;
}
However with this approach your duplicate keys will get overriden like so:
[
{
"Id": "1",
"Time": "18:40",
"Topic": "yyyyyyyyyyyy",
"GUEST": {
"Role": "GS",
"Infos": "Colorado"
}
},
{
"Id": "2",
"Time": "11:32",
"Topic": "xoxo",
"GUEST": {
"Role": "GS",
"Infos": "Nevada"
}
}
]
I think this is a good start and you can fine tune parsing GUEST part up to your liking
JSON's format is wrong.
Key-value pair array is not allowed in JSON.
Key-value pair array cannot have same name key.
Change to the following format (JSON has strict requirements on the format):
myJson = [
{
"Id" : "1",
"Time" : "18:40",
"Topic" : "yyyyyyyyyyyy",
"GUEST":
[
{
"Role" : "HS",
"Infos" : "Arizona"
},
{
"Role" : "GS",
"Infos" : "Arizona"
},
{
"Role" : "HS",
"Infos" : "Colorado"
}
]
}
]

Compare,add,update,delete elements on array of mongodb

Need help on operation like update,delete,add,upsert,delete on below document of MongoDB.
Below is MongoDB document that exists in temp collection.
{
"local_id" : "1841",
"name_first" : "tiger",
"name_last" : "lion",
"address" : [
{
"id" : 1,
"address_type" : "Home",
"city" : "Delhi",
"country" : "",
"po_box" : ""
},
{
"id" : 2,
"address_type" : "Work",
"city" : "",
"country" : "",
"po_box" : ""
}
],
"email" : [
{
"email_id" : "blah#gmail.com",
"id" : 1,
"type" : "Home"
},
{
"email_id" : "Pearl1#gmail.com",
"id" : 2,
"type" : "Work"
}
],
"phone_number" : [
{
"id" : 1,
"no" : "+911234567890",
"type" : "Mobile"
},
{
"id" : 2,
"no" : "+917894561230",
"type" : "work"
}
]
}`
Now I have some document like below, i want query that will compare,add,update,delete on my above document.
`
{
"local_id" : "1730",
"name_first" : "lion",
"name_last" : "king",
"address" : [
{
"id" : 1,
"address_type" : "Home",
"city" : "Delhi",
"country" : "India",
"po_box" : "110041"
},
{
"id" : 2,
"address_type" : "Work",
"city" : "Delhi-NCR",
"country" : "India",
"po_box" : "110048"
},
{
"id" : 3,
"address_type" : "Work",
"city" : "Delhi-NCR",
"country" : "Indai",
"po_box" : "110048"
}
],
"email" : [
{
"email_id" : "updatethis#gmail.com",
"id" : 1,
"type" : "Home"
},
{
"email_id" : "Pearl1#gmail.com",
"id" : 2,
"type" : "Work"
},
{
"email_id" : "addthisarray#gmail.com",
"id" : 3,
"type" : "personal"
}
],
"phone_number" : [
{
"id" : 1,
"no" : "+911234567890",
"type" : "Mobile"
}
/*second array not here so remove that array from that document*/
]
}`
You can save function on server as you can call that function to get the differences, as below.
db.system.js.save({
_id: "getupdatedArray",
value: function(obj1, obj2) {
var VALUE_CREATED = 'created';
var VALUE_UPDATED = 'updated';
var VALUE_DELETED = 'deleted';
var VALUE_UNCHANGED = 'unchanged';
function map(obj1, obj2) {
if (isValue(obj1) || isValue(obj2)) {
return {
type: compareValues(obj1, obj2),
old: obj1,
new: obj2
};
}
var diff = {};
for (var key in obj1) {
if (isFunction(obj1[key])) {
continue;
}
var value2 = undefined;
if ('undefined' != typeof(obj2[key])) {
value2 = obj2[key];
}
diff[key] = map(obj1[key], value2);
}
for (var key in obj2) {
if (isFunction(obj2[key]) || ('undefined' != typeof(diff[key]))) {
continue;
}
diff[key] = map(undefined, obj2[key]);
}
return diff;
}
function compareValues(value1, value2) {
if (value1 === value2) {
return VALUE_UNCHANGED;
}
if ('undefined' == typeof(value1)) {
return VALUE_CREATED;
}
if ('undefined' == typeof(value2)) {
return VALUE_DELETED;
}
return VALUE_UPDATED;
}
function isFunction(obj) {
return {}.toString.apply(obj) === '[object Function]';
}
function isArray(obj) {
return {}.toString.apply(obj) === '[object Array]';
}
function isObject(obj) {
return {}.toString.apply(obj) === '[object Object]';
}
function isValue(obj) {
return !isObject(obj) && !isArray(obj);
}
return map(obj1, obj2);
}
})
Then you can call function as below..
db.loadServerScripts();
getupdatedArray({"a": "abc"}, {"a": "a111", "b": "bbb"});
This will give you result as below:
{
"a" : {
"type" : "updated",
"old" : "abc",
"new" : "a111"
},
"b" : {
"type" : "created",
"old" : undefined,
"new" : "bbb"
}
}
Thanks
Satish Lakhani

Trying to get the sum for each Object

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);
});

merging two data structures of different shapes

I have two data structures with different shapes that come from two different API's. The data is in JSON format, language is JavaScript.
Array 1:
[ { "document" : { "html" : "some_html", "name" : "DOCUMENT_NAME_1" },
"tooltips" : [ { "html" : "some_html", "name" : "TASK_NAME_1" },
{ "html" : "some_html", "name" : "TASK_NAME_2" } ] },
{ "document" : { "html" : "some_html", "name" : "DOCUMENT_NAME_2" },
"tooltips" : [ { "html" : "some_html", "name" : "TASK_NAME_3" },
{ "html" : "some_html", "name" : "TASK_NAME_4" } ] }]
Array 2:
[ [ { "name" : "TASK_NAME_1", "status" : "FINISHED" },
{ "name" : "TASK_NAME_2", "status" : "OPEN" } ],
[ { "name" : "TASK_NAME_3", "status" : "OPEN" },
{ "name" : "TASK_NAME_4", "status" : "FUTURE" } ] ]
The elements of the tooltips field from the array 1 contain the same "name"s as elements of array 2. How can I elegantly merge "status" from the array 2 into tooltips within array 1?
I thought that lenses could be the right answer, but I'm unsure as I've never used them.
I am aware of some ways I could solve it using nested iteration and updating array 1. I am ideally looking for an approach that doesn't modify existing data structures.
This is little complex but it should work for you
array2.forEach(function(tooltips){
tooltips.forEach(function(tooltip){
for (var i = 0; i < array1.length; i++) {
for (var j = 0; j < array1[i].tooltips.length; j++) {
var arr1Tooltip = array1[i].tooltips[j];
if(arr1Tooltip.name == tooltip.name)
arr1Tooltip.status = tooltip.status;
};
};
});
});
console.log(JSON.stringify(array1));
This is probably way over engineered and not very efficient, but you can do it as with this JSFiddle using recursive functions. I'm too tired to do it in a clever way.
var arr1 = [ { "document" : { "html" : "some_html", "name" : "DOCUMENT_NAME_1" },
"tooltips" : [ { "html" : "some_html", "name" : "TASK_NAME_1" },
{ "html" : "some_html", "name" : "TASK_NAME_2" } ] },
{ "document" : { "html" : "some_html", "name" : "DOCUMENT_NAME_2" },
"tooltips" : [ { "html" : "some_html", "name" : "TASK_NAME_3" },
{ "html" : "some_html", "name" : "TASK_NAME_4" } ] }];
var arr2 = [ [ { "name" : "TASK_NAME_1", "status" : "FINISHED" },
{ "name" : "TASK_NAME_2", "status" : "OPEN" } ],
[ { "name" : "TASK_NAME_3", "status" : "OPEN" },
{ "name" : "TASK_NAME_4", "status" : "FUTURE" } ] ];
var findStatus = function(name, searchArray) {
var r = '';
if (typeof searchArray === 'object') {
if ("name" in searchArray && "status" in searchArray) {
if (searchArray.name == name) {
return searchArray.status;
} else {
return '';
}
} else {
for (var i in searchArray) {
r = findStatus(name, searchArray[i]);
if (r != '') {
return r;
}
}
}
}
return '';
};
var updateStatus = function(arrToUpdate, arrWithStatus) {
var copy = $.extend(true, {}, arrToUpdate);
var r = '';
if (typeof copy === 'object') {
if ("name" in copy) {
r = findStatus(copy.name, arrWithStatus);
if (r != '') {
copy.status = r;
}
} else {
for (var i in copy) {
copy[i] = updateStatus(copy[i], arrWithStatus);
}
}
}
return copy;
};
var arr3 = updateStatus(arr1, arr2); // Final combined array
I added the var copy = $.extend(true, {}, arrToUpdate); line so that it will do a deep copy and not modify the original array, as a result, it requires jQuery.
Since your data structure is nested, you will need two zip.map/zipWiths:
zip(array1, array2).map(function([obj, tooltips]) { // ES6 destructuring syntax
return {
document: obj.document,
tooltips: zip(obj.tooltips, tooltips).map(function([tooltip, extender]) {
return {
html: tooltip.html,
name: tooltip.name,
status: extender.status
};
})
};
})
If you don't like to repeat those object literal structures, you might be able to use some copying function; for example
extend({}, document, {tooltips:…})
extend({}, tooltip, extender);
You also might use a lenses library like https://github.com/DrBoolean/lenses or https://github.com/fantasyland/fantasy-lenses for that, but I'm not sure whether that's worth the effort - the above code only needs underscore/lodash.
To get around the inner zipWith, you would need a Traversal lens (I assume you're familiar with this article), but I haven't yet seen a JavaScript library that offers such.

Categories

Resources