I have got a task to iterate through the complex json file that contains json array. I could not access the array object from the json file.
I need to access the particularly the class-name object from the json
file.
classdetail.json
[ [ { "student" : [
{
"name" : "AAaa",
"class-name" : "A",
"grade-label" : "AA" },
{
"name" : "AAbb",
"class-name" : "A",
"grade-label" : "AB" },
{
"name" : "AAcc",
"class-name" : "A",
"grade-label" : "AB" },
{
"name" : "AAdd",
"class-name" : "B",
"grade-label" : "AA" } ],
"Average" : 2.5 },
{
"student" : [
{
"name" : "BBaa",
"class-name" : "B",
"grade-label" : "AB" },
{
"name" : "BBbb",
"class-name" : "B",
"grade-label" : "AA" },
{
"name" : "BBcc",
"class-name" : "B",
"grade-label" : "AA" },
{
"name" : "BBdd",
"class-name" : "B",
"grade-label" : "AA" } ],
"Average" : 2.5 } ] ]
iterate.js
var fs = require('fs');
var express = require('express');
var http = require('http');
var publicApis;
var item;
var subItem;
classmem = JSON.parse(fs.readFileSync("classdetail.json", "utf8"));
for (item in classmem) {
for (subItem in classmem[item]) {
console.log(classmem[item][subItem]);
}
}
for (item in classmem) {
for (subItem in classmem[item]) {
var student = classmem[item][subItem].student;
for (row in student) {
console.log(student[row]['class-name']);
}
}
}
But read about Array.forEach.
for...in iterates over object properties in arbitrary order. It might not be what you want to use for an array, which stores items in a well-defined order. (though it should work in this case)
Try Array.forEach():
// iterate over `classmem`
classmem.forEach(function(element, index, array) {
// iterate over classmem[index], which is an array too
element.forEach(function(el, idx, arr) {
// classmem[index][idx] contains objects with a `.student` property
el.student.forEach(function(e, i, a) {
console.log(e["name"], e["class-name"], e["grade-label"]);
});
});
});
first check the value is array then access to the "class-name" value
for (item in classmem) {
for (subItem in classmem[item]) {
**if (typeof classmem[item][subItem] === 'object') {
classmem[item][subItem].forEach(function (val, ind) {
console.log(val['class-name']);
});
}**
}
}
Related
If I have an object like this in javascript, how can I count the number of elements in [A, B, C, D, etc.]?
{ "test" : [ "A", "B", "C", "D", ... ] }
And if i have:
{ "_id" : "1", "value" : { "test" : [ "A", "B", "C", "D" ] } }
{ "_id" : "2", "value" : { "test" : [ "A", "B", "C", "D", "E", "F" ] } }
How can I convert it to the following structure?
{ "_id" : "1", "value" : 4 }
{ "_id" : "2", "value" : 6 }
Thanks
Try the following:
var data = [{ "_id" : "1", "value" : { "test" : [ "A", "B", "C", "D" ] } },{ "_id" : "2", "value" : { "test" : [ "A", "B", "C", "D", "E","F" ] } }];
var arr = data.map((d) => {
d["value"] = d["value"].test.length;
return d;
});
console.log(arr);
EDIT
The more smart way is to simply use a forEach loop in the following way
var data = [{ "_id" : "1", "value" : { "test" : [ "A", "B", "C", "D" ] } },{ "_id" : "2", "value" : { "test" : [ "A", "B", "C", "D", "E","F" ] } }];
data.forEach((d) => {
d["value"] = d["value"].test.length;
});
console.log(data);
If can always do .length on an object check the example below:
var obj = { "test" : [ "A", "B", "C", "D", "E", "F"] };
console.log(obj.test.length)
EDIT:
For your second question
you can do
obj.value = obj.value.test.length;
var obj = { "_id" : "2", "value" : { "test" : [ "A", "B", "C", "D", "E", "F" ] } };
obj.value = obj.value.test.length;
console.log(obj)
As the answer changed from generic javascript to mongodb, I have to update the answer to reflect the question:
Instead of using map/reduce in mongo, consider using the aggregation framework mongodb provides, it allows for very powerful querying.
In this case, you want the $size operator
db.yourCollection.aggregate([
{ $project: { value: { $size: "$value.test" } } }
])
Unless explicitly removed, mongodb will always include the document _id so for this use case we only need to address the value, if you need any other value from the documents in your result-set, add them like:
db.yourCollection.aggregate([
{ $project: {
value: { $size: "$value.test" },
foo: '$foo',
bar: '$bar',
baz: '$baz',
} }
])
Original answer (pure js, no mongodb)
I suspect the example is actually part of an array
{ "_id" : "1", "value" : { "test" : [ "A", "B", "C", "D" ] } }
{ "_id" : "2", "value" : { "test" : [ "A", "B", "C", "D", "E" ] } }
So I assume you want to map a large set of records this way, and you don't want to (accidentally) destroy the original value property (as other answers do), here's how I'd do this:
const list = [
{ "_id" : "1", "value" : { "test" : [ "A", "B", "C", "D" ] } },
{ "_id" : "2", "value" : { "test" : [ "A", "B", "C", "D", "E" ] } }
];
// map the list into a new list of object containing the _id and value count
const mapped = list.map((record) => {
return {
_id: record._id,
// assuming there's only test and it's an array
value: record.value.test.length,
};
});
If value inside each record is an object which may contain zero or more arrays and you wish to count the total amount of items inside all of them, you can reduce the arrays into a total number.
const list = [
{ "_id" : "1", "value" : { "test" : [ "A", "B", "C", "D" ] } },
{ "_id" : "2", "value" : { "test" : [ "A", "B", "C", "D", "E" ] } },
{ "_id" : "3", "value" : { } },
{ "_id" : "4", "value" : { "test" : [ "A", "B" ], "test2" : [ "C", "D", "E" ] } },
];
// map the list into a new list of object containing the _id and value count
const mapped = list.map((record) => {
return {
_id: record._id,
// assuming there's zero or more arrays
value: Object.keys(record.value)
// filter out any non-array
.filter((key) => Array.isArray(record.value[key]))
// reduce the arrays into their combined length
.reduce((carry, key) => carry + record.value[key].length, 0),
};
});
console.log(mapped);
You can transform your array with a simple forEach loop - like below:
var data = [{
"_id": "1",
"value": {
"test": ["A", "B", "C", "D"]
}
},
{
"_id": "2",
"value": {
"test": ["A", "B", "C", "D", "E"]
}
}
];
var dataCount = [];
data.forEach(function(element) {
var newElement = {};
newElement._id = element._id;
newElement.value = element.value.test.length;
dataCount.push(newElement);
//console.log(newElement);
});
console.log(dataCount);
I have a example.json file.
{
"tc" :
[
{"name" : "tc_001"},
{"name" : "tc_002"},
{"name" : "tc_003"},
{"name" : "tc_004"},
{"name" : "tc_005"}
]
}
In here I need to add another array to the tc[0]th index. In Node JS I tried this:
var fs = require('fs');
var obj = JSON.parse(fs.readFileSync('example.json', 'utf8'));
var arr1 = {bc :[{name:'bc_001'}]}
var arr2 = {bc :[{name:'bc_002'}]}
obj.tc[0] = arr1;
obj.tc[0] = arr2;
But when printing that obj, obj.tc[0] has replaced by value of arr2.
The final result I want to achieve is:
{
"tc" :
[
{
"name" : "tc_001"
"bc" :
[
{"name" = "bc_001"},
{"name" = "bc_002"}
]
},
{"name" : "tc_002"},
{"name" : "tc_003"},
{"name" : "tc_004"},
{"name" : "tc_005"}
]
}
I also want to write this back to the same json file.
I'm trying to achieve here a file containing number of tcs with unique names. Furthermore one tc can have multiple bcs, and bc has a name attribute.
I also accept suggestion on a better json structure to support this concept.
A solution for more than one item to add to the object.
It uses an object o where the new values should go and a value object v with the keys and the items to assign.
Inside it iterates over the keys and build a new array if not already there. Then all values are pushed to the array.
function add(o, v) {
Object.keys(v).forEach(k => {
o[k] = o[k] || [];
v[k].forEach(a => o[k].push(a));
});
}
var obj = { "tc": [{ "name": "tc_001" }, { "name": "tc_002" }, { "name": "tc_003" }, { "name": "tc_004" }, { "name": "tc_005" }] };
add(obj.tc[0], { bc: [{ name: 'bc_001' }] });
add(obj.tc[0], { bc: [{ name: 'bc_002' }] });
document.write('<pre>' + JSON.stringify(obj, 0, 4) + '</pre>');
In my angularjs application, I have the following list of objects.
$scope.itemsList = [
{
"setupId": "T_2893",
"name" : "abc"
},
{
"setupId": "LBT826",
"name" : "xyz"
},
{
"setupId": "LBT1252",
"name" : "pqr"
},
{
"setupId": "G1252",
"name" : "dwr"
}
]
Now when I call $scope.changeOreder(1, 3) function it should reorder the objects based on the prev and next index. so the list should be as follows.
$scope.itemsList = [
{
"setupId": "T_2893",
"name" : "abc"
},
{
"setupId": "LBT1252",
"name" : "pqr"
},
{
"setupId": "G1252",
"name" : "dwr"
},
{
"setupId": "LBT826",
"name" : "xyz"
}
]
Now if I call, $scope.changeOreder(2, 0), the new list should be,
$scope.itemsList = [
{
"setupId": "G1252",
"name" : "dwr"
},
{
"setupId": "T_2893",
"name" : "abc"
},
{
"setupId": "LBT1252",
"name" : "pqr"
},
{
"setupId": "LBT826",
"name" : "xyz"
}
]
In my $scope.changeOrder function, I have tried different ways, like taking the back up of the object at prevIndex , then deleting the obj at prevIndex to insert the backed up obj at newIndex, but because I have deleted the object the newIndex is no more valid in the current list!!!. Like this I tried different different ways but the final list is not getting ordered the way I am expecting. Can any one help me in fixing it.
Moving an item to a specified index:
var items = [
{
"setupId": "G1252",
"name" : "dwr"
},
{
"setupId": "T_2893",
"name" : "abc"
},
{
"setupId": "LBT1252",
"name" : "pqr"
},
{
"setupId": "LBT826",
"name" : "xyz"
}
];
function moveItem(posA, posB) {
/*
* If an item is moved from a higher index to a lower index, then we need to
* remove the current item first, then add it.
* When moving a item from a low index to a higer index, then we need to add
* the item first, then delete it.
*/
var upToDown = posA > posB;
var itemToMove = items[posA];
//Make copy first
var tmpList = items.slice(0);
if (!upToDown) {
//Add item to specified index
tmpList.splice(posB+1, 0, itemToMove);
//Remove the old item
tmpList.splice(posA, 1);
} else {
//Remove the old item
tmpList.splice(posA, 1);
//Add item to specified index
tmpList.splice(posB, 0, itemToMove);
}
return tmpList;
}
var result = moveItem(0, 3);
console.log(result);
Plunk: https://plnkr.co/edit/23DGzcXBEY7qrqFWsQNA?p=preview
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.
I have one JSON Object like this :
var myObject = [
{
"Name" : "app1",
"id" : "1",
"groups" : [
{ "id" : "test1",
"name" : "test group 1",
"desc" : "this is a test group"
},
{ "id" : "test2",
"name" : "test group 2",
"desc" : "this is another test group"
}
]
},
{
"Name" : "app2",
"id" : "2",
"groups" : [
{ "id" : "test3",
"name" : "test group 4",
"desc" : "this is a test group"
},
{ "id" : "test4",
"name" : "test group 4",
"desc" : "this is another test group"
}
]
},
{
"Name" : "app3",
"id" : "3",
"groups" : [
{ "id" : "test5",
"name" : "test group 5",
"desc" : "this is a test group"
},
{ "id" : "test6",
"name" : "test group 6",
"desc" : "this is another test group"
}
]
}
];
I have new value available of "name" for specific "id".
How can I replace "name" of specific "id" inside any object ?
And how to count total number of groups among all objects ?
for example : replace name to "test grp45" for id = "test1"
Here is fiddle
http://jsfiddle.net/qLTB7/21/
The following function will search through an object and all of its child objects/arrays, and replace the key with the new value. It will apply globally, so it won't stop after the first replacement. Uncomment the commented line to make it that way.
function findAndReplace(object, value, replacevalue) {
for (var x in object) {
if (object.hasOwnProperty(x)) {
if (typeof object[x] == 'object') {
findAndReplace(object[x], value, replacevalue);
}
if (object[x] == value) {
object["name"] = replacevalue;
// break; // uncomment to stop after first replacement
}
}
}
}
Working jsfiddle: http://jsfiddle.net/qLTB7/28/
Try this
function findAndReplace(object,keyvalue, name) {
object.map(function (a) {
if (a.groups[0].id == keyvalue) {
a.groups[0].name = name
}
})
}
findAndReplace(myObject,"test1" ,"test grp45");
Here's a different approach using Array.prototype.some. It assumes that the Name property in the outer objects should be actually be name (note capitalisation).
function updateNameById(obj, id, value) {
Object.keys(obj).some(function(key) {
if (obj[key].id == id) {
obj[key].name = value;
return true; // Stops looping
}
// Recurse over lower objects
else if (obj[key].groups) {
return updateNameById(obj[key].groups, id, value);
}
})
}
The advantage of some is that it stops as soon as the callback returns true.
I think this should work for you:-
var id = 'test1';
var newname = 'test grp45';
var numberOfGruops = 0;
myObject.forEach(function(app){
numberOfGruops += app.groups.length; //Count all groups in this app
app.groups.forEach(function(group){
if(group.id===id)
group.name = newname; // replace the name
});
});
Maybe a more succinct sol'n
function changeName(objArray, objId, newName) {
objArray.forEach(function(obj) {
if (obj.id === objId) obj.Name = newName;
});
}
Personally: if this were me, when creating these objects, I would create a new obj and key them by id.
var myApps = {};
myObject.forEach(function(o) {
myApps[o.id] = o;
});
=>
{
"1": {
"Name": "app1",
"id": "1",
"groups": [
{
"id": "test1",
"name": "test group 1",
"desc": "this is a test group"
},
{
"id": "test2",
"name": "test group 2",
"desc": "this is another test group"
}
]
}
}
And then you could just do:
myApps['someId'].name = 'This is my new Name'
Check it out here:
http://jsfiddle.net/qLTB7/40/
it should be if (object["id"] == value) instead of if (object[x] == value) in 7th line of PitaJ answer, so whole function will look like:
function findAndReplace(object, value, replacevalue) {
for (var x in object) {
if (object.hasOwnProperty(x)) {
if (typeof object[x] == 'object') {
findAndReplace(object[x], value, replacevalue);
}
if (object["id"] == value) {
object["name"] = replacevalue;
// break; // uncomment to stop after first replacement
}
}
}
}
if you leave object[x] - function will replace name also for objects with other keys values set to "test1", for example
{"id": "xxx", "name": "test group 1", "desc": "test1"}