I have the following JSON object. I need to remove the duplicates and merge the inner object using plain Javascript. How do I go about doing this?
[{
"id" : 1,
"name" : "abc",
"nodes" :[
{
"nodeId" : 20,
"nodeName" : "test1"
}
]
},
{
"id" : 1,
"name" : "abc",
"nodes" :[
{
"nodeId" : 21,
"nodeName" : "test2"
}
]
}]
Following is the object that I expect as output.
[{
"id" : 1,
"name" : "abc",
"nodes" :[
{
"nodeId" : 20,
"nodeName" : "test1"
},
{
"nodeId" : 21,
"nodeName" : "test2"
},
]
}]
Regards.
Shreerang
First turn the JSON into a Javascript array so that you can easily access it:
var arr = JSON.parse(json);
Then make an array for the result and loop through the items and compare against the items that you put in the result:
var result = [];
for (var i = 0; i < arr.length; i++) {
var found = false;
for (var j = 0; j < result.length; j++) {
if (result[j].id == arr[i].id && result[j].name == arr[i].name) {
found = true;
result[j].nodes = result[j].nodes.concat(arr[i].nodes);
break;
}
}
if (!found) {
result.push(arr[i]);
}
}
Then you can create JSON from the array if that is the end result that you need:
json = JSON.stringify(result);
If your string is called input, then this:
var inter = {};
for (var i in input) {
if (!inter.hasOwnProperty(input[i].name)) {
inter[input[i].name] = input[i].nodes;
inter[input[i].name].name = input[i].name;
inter[input[i].name].id = input[i].id;
}
else
inter[input[i].name] = inter[input[i].name].concat(input[i].nodes)
}
results in:
{"abc":[{"nodeId":20,"nodeName":"test1"},{"nodeId":21,"nodeName":"test2"}]}
You can see how I'm using an intermediate object keyed by whatever the match criterion is. It's an object rather than an array as you asked, but you can iterate it anyway. In fact you're probably better off with this structure than the array you asked for.
BTW, I shoved a couple of text-named properties: "id" and "name" into an array there. They don't show up in JSON.stringify but they're there.
Related
I have this javascript objects :
var countryArray = [{
"country" : 'Indonesia',
"state" : ['DKI','Bali'],
},
{
"country" : 'Malaysia',
"state" : ['Penang','Johor'],
}];
var newArr = [{ "country" : 'Malaysia', "state" : ['Kelantan'] }]
How can I merge or add newArr to the related CountryArray.
Expected result :
var countryArray = [{
"country" : 'Indonesia',
"state" : ['DKI','Bali'],
},
{
"country" : 'Malaysia',
"state" : ['Penang','Johor','Kelantan'],
}];
concat ?
countryArray = countryArray.concat(newArr);
EDIT
Ok, I see, you want to update states of countryArray according to what is in newArr, no more concat:
EDIT2
concat as you want to add states of countryArray according to what is in newArr
var countryArray = [{
"country" : "Indonesia",
"state" : ["DKI","Bali"],
},
{
"country" : "Malaysia",
"state" : ["Penang","Johor"],
}];
var newArr = [{ "country" : "Malaysia", "state" : ["Kelantan"] }];
alert("Before while: " + countryArray[1]["state"]);
var i=0;
while(countryArray[i]) {
var j=0;
while(newArr[j]) {
if(countryArray[i]["country"] == newArr[j]["country"]) {
countryArray[i]["state"] = countryArray[i]["state"].concat(newArr[j]["state"]);
}
j++;
}
i++;
}
alert("After while: " + countryArray[1]["state"]);
Basically you want a variation of JavaScript merging objects by id which merges the array values.
Create a hash table.
Iterate both arrays and store the data in the hash table, indexed by the ID. If there already is some data with that ID, merge it.
Get an array with the values of the hash map.
var countryArray = [{
"country" : 'Indonesia',
"state" : ['DKI','Bali'],
}, {
"country" : 'Malaysia',
"state" : ['Penang','Johor'],
}];
var newArr = [{
"country" : 'Malaysia',
"state" : ['Kelantan']
}];
function mergeById(objs, id) {
var hash = new Map();
objs.forEach(function(obj) {
var merged = hash.get(obj[id]) || {};
Object.keys(obj).forEach(function(key) {
if (key === id) return merged[id] = obj[id];
if (!merged[key]) merged[key] = [];
if (Array.isArray(obj[key])) [].push.apply(merged[key], obj[key]);
else merged[key].push(obj[key]);
})
hash.set(obj[id], merged)
});
return Array.from(hash.values());
}
console.log(mergeById(countryArray.concat(newArr), "country"));
If you want to merge, sometimes you need to splice as well. Try this :
var countryArray = [{
"country" : "Indonesia",
"state" : ["DKI","Bali"],
},
{
"country" : "Malaysia",
"state" : ["Penang","Johor"],
}];
To merge with incoming new data ;
var newArr = [{ "country" : "Malaysia", "state" : ["Kelantan"] }];
MergeArray(countryArray,newArr);
console.table(countryArray);
To Splice form the incoming data ;
var DelArray = [{ "country" : "Malaysia", "state" : ["Penang"] }];
SpliceArray(countryArray,DelArray);
console.table(countryArray);
and the related function ;
function MergeArray(countryArray,newArr) {
var a = 0;
$.each(newArr, function (key, data1) {
var b = 0;
$.each(countryArray, function (key, data2) {
if(data1.country == data2.country) { // match the same country
countryArray[b]["state"] = countryArray[b]["state"].concat(newArr[a]["state"]);
}
b++; });
a++; });
}
function SpliceArray(countryArray,DelArray) {
var a=0;
$.each(DelArray, function (key, data1) {
var b=0;
$.each(countryArray, function (key, data2) {
if(data1.country == data2.country) { // get same country
console.log(countryArray[b]["state"]) // ["Penang", "Johor", "Kelantan"]
for(var c=0; c < countryArray[b]["state"].length; c++){ // loop in countryArray state[]
console.log(DelArray[a]['state']); // to remove : ["Penang"]
if(countryArray[b]["state"][c] == DelArray[a]['state'] ) {
countryArray[b]["state"].splice(c,1); // remove ["Penang"]
}
}
}
b++;
});
a++;
});
}
hope will help
I'm trying to sort a JSON into multiple arrays based on type, my current json is:
// current json file:
{
"res": [
{
"type" : "stream",
"price" : "3.99",
"id" : "13nee"
},
{
"type" : "stream",
"price" : "2.99",
"id" : "8ejwj"
},
{
"type" : "buy",
"price" : "3.99".
"id" : "9akwk"
},
...
]
}
I'm looking to sort it into multiple arrays by type like below:
var sorted = {
"stream" : [
{
"price" : "2.99",
"id" : "8ejwj"
},
{
"price" : ".99",
"id" : "13nee"
},
... // other objects with type: "stream"
],
"buy" : [
{
"price" : "3.99".
"id" : "9akwk"
},
... // other objects with type: "buy"
]
}
I've tried it, but the only solution I can think of is by cases - run if loop, if case matches type, then push object to array. Is there a more elegant solution?
var items = {};
var i = 0;
for(i; i < res.length; i += 1){
var resItem = res[i];
if(items.hasOwnProperty(resItem.type)){
items[resItem.type].push({price:resItem.price, id:resItem.id});
} else {
items[resItem.type] = [{price:resItem.price, id:resItem.id}];
}
}
The properties on JavaScript objects are hashed, so you can dynamically match and generate new objects like above. If you want to apply a well ordering sort, you'll need to apply it to the arrays of the newly generated items object.
Step 1 :
Convert the JSON to a jquery object :
var x = jQuery.parseJSON( jsonString );
Step 2:
Use underscore library's _.groupBy to group :
_.groupBy(x,'type');
There might be some adjustment you need to do for x being array or object.
Edit :
You don't need step1. Just do :
sorted = _.groupBy(json.res,'type');
You could do something like this with ECMA5. This performs, generically, the sort and reduce that you have indicated in your question, so you can add more fields to your data without having to change the routine. It also leaves your original data intact.
Javascript
var original = {
'res': [{
'type': 'stream',
'price': '3.99',
'id': '13nee'
}, {
'type': 'stream',
'price': '2.99',
'id': '8ejwj'
}, {
'type': 'buy',
'price': '3.99',
'id': '9akwk'
}]
},
sorted = {};
original.res.slice().sort(function (a, b) {
a = +(a.price);
b = +(b.price);
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
}).reduce(function (acc, element) {
if (!acc[element.type]) {
acc[element.type] = [];
}
acc[element.type].push(Object.keys(element).filter(function (name) {
return name !== 'type';
}).reduce(function (prev, name) {
prev[name] = element[name];
return prev;
}, {}));
return acc;
}, sorted);
console.log(JSON.stringify(sorted));
Output
{
"stream": [{
"price": "2.99",
"id": "8ejwj"
}, {
"price": "3.99",
"id": "13nee"
}],
"buy": [{
"price": "3.99",
"id": "9akwk"
}]
}
On jsFiddle
I want to populate an array of objects, so that the array should be attached to a grid. This is the format that I require:
data.push({ id: 1, values: { "country": "uk", "age": 33, "name": "Duke", "firstname": "Patience", "height": 1.842, "email": "patience.duke#gmail.com", "lastvisit": "11\/12\/2002" } });
I have key-value pairs of values, I am not sure exactly how would I be constructing the above object in a loop...??
EDIT:
I have this so far, but I am only adding values, not their respective keys:
var recordValues = [];
//for loop iterator
recordValues.push(colValues[colCount]);
//end for
data.push({ id: recordID, values: recordValues });
colValues contain the following: "uk", 33, "Duke"...all the possible values in an object..
If understand you correctly:
var data = [];
for (var i = 0; i < people.length; i++) {
var person = people[i];
data.push({
id : i + 1,
values : {
country : person.country,
age : person.age,
name : person.surname,
firstname : person.firstname,
height : person.height,
email : person.email,
lastvisit : person.lastvisit
}
});
}
for (var key in keyValuePairs) {
if (keyValuePairs.hasOwnProperty(key)) {
data.push({ id: key, values:keyValuePairs[key]);
}
}
hasOwnProperty filters out prototype properties
You could do something like this:
var recordValues = [];
for (var value in colValues) {
recordValues.push(colValues[value]);
}
data.push({ id: recordID, values: recordValues });
I have several objects like this:
I want to move type and value one step up so they will be next to field, and then delete data.
It looks like this when departments is converted to JSON:
[
{"field" : "DEPARTMAN_NO",
"data" : { "type":"numeric" , "comparison":"eq" , "value":11 }
},
{"field" : "DEPARTMAN_ADI",
"data" : { "type":"string" , "value":"bir" }
}
]
I have tried:
departments = grid.filters.getFilterData();
i = {};
for(var i in department) {
department = i.data;
delete.department.data;
};
but it dosen't work.
1) First, loop departments, each item we call it department;
2) You want to move department.data's properties to department, From another angle, you can move department's properties to department.data and return department.data, code like:
var departments = [{
"field": "DEPARTMAN_NO",
"data": {
"type": "numeric",
"comparison": "eq",
"value": 11
}
}, {
"field": "DEPARTMAN_ADI",
"data": {
"type": "string",
"value": "bir"
}
}],
department;
for (var i = 0, len = departments.length; i < len; i++) {
department = departments[i]; // department
for (var key in department) {
if (key !== 'data' && department.data) {
department.data[key] = department[key];
}
}
departments[i] = department.data || department; // if no department.data, no change
}
console.log(departments);
result:
view the full demo http://jsfiddle.net/KVYE5/
I wrote a little npm package that does what you're asking for: moving a property up a level in an object.
You can get it here: https://www.npmjs.com/package/move-property-up-a-level
Usage
var movePropertyUpALevel = require('movePropertyUpALevel');
var fakeObj = {
poodle: {
first: {
hey: 'you'
},
second: 'meAgain'
}
};
movePropertyUpALevel(fakeObj, 'poodle');
console.log(fakeObj.first.hey);
//'you'
console.log(fakeObj.poodle);
//undefined
obj =
[
{"field" : "DEPARTMAN_NO",
"data" : { "type":"numeric" , "comparison":"eq" , "value":11 }
},
{"field" : "DEPARTMAN_ADI",
"data" : { "type":"string" , "value":"bir" }
}
];
for ( var item in obj ) {
if ( obj[item].field && obj[item].data ) { //check the 'field' and 'data' exist
obj[item].field = {
dept : obj[item].field , //department name is put into a property
type : obj[item].data.type, //so is data.type and data.value..
value: obj[item].data.value //..all are now contained in 'field'
};
delete obj[item].data; //remove the 'data' object
}
}
console.log(obj);
department.type = department.data.type;
department.value = department.data.value;
delete department['data'];
I need to convert a hash map
{
"fruit" : ["mango","orange"],
"veg" : ["carrot"]
}
to
[
{ "type" : "fruit" , "name" : ["mango","orange"] } ,
{ "type" : "veg" , "name" : ["carrot"] }
]
how do I do that??
You can do it like this (in a working snippet):
var input = {
"fruit" : ["mango","orange"],
"veg" : ["carrot"]
}
var output = [], item;
for (var type in input) {
item = {};
item.type = type;
item.name = input[type];
output.push(item);
}
// display result
document.write(JSON.stringify(output));
Or, if you or someone else has been extending the Object prototype with enumerable properties (which I think is a bad practice personally), then you could use this to protect from that:
var input = {
"fruit" : ["mango","orange"],
"veg" : ["carrot"]
}
var output = [], item;
for (var type in input) {
if (input.hasOwnProperty(type)) {
item = {};
item.type = type;
item.name = input[type];
output.push(item);
}
}
// display result
document.write(JSON.stringify(output));
And, using some more modern functionality:
var input = {
"fruit" : ["mango","orange"],
"veg" : ["carrot"]
};
var output = Object.keys(input).map(function(key) {
return {type: key, name: input[key]};
});
// display the result
document.write(JSON.stringify(output));
In a browser that supports ES5 – or where you added a shim for it:
var stuff = {
"fruit" : ["mango","orange"],
"veg" : ["carrot"]
}
var array = Object.keys(stuff).map(function(key) {
return {"type" : key, "name" : stuff[key] }
})
See: Object.keys, Array's map
Or, in the old fashion way:
var stuff = {
"fruit" : ["mango","orange"],
"veg" : ["carrot"]
}
var array = []
for (var key in stuff) {
if (stuff.hasOwnProperty(key)) {
array.push({"type" : key, "name" : stuff[key] })
}
}
Please notice that in both cases the array's value are shared because in JS the objects are passed by reference. So, for instance, stuff["fruit"] and array[0].name points to the same reference of the array ["mango", "orange"]. It means, if you change one of them, the other will be changed as well:
stuff["fruit"].push("apple");
alert(array[0].name); // "mango", "orange", "apple"
To avoid that, you can use slice to have a one-level deep copy of your array. So in the code above, instead of:
"name" : stuff[key]
you will have:
"name" : stuff[key].slice(0)
Hope it helps.
For those using ES6 maps...
Assuming you have...
const m = new Map()
m.set("fruit",["mango","orange"]);
m.set("veg",["carrot"]);
You can use...
const arr = Array.from(map, ([key, val]) => {
return {type: key, name: val};
});
Note that Array.from takes iterables as well as array-like objects.
I would like to give an "oneline" solution:
var b = Object.keys(a).map(e => { return { type:e, name:a[e] } });
Economy of words at your service. Question asked for translating an object to an array, so I'm not duplicating above answer, isn't it?
It looks simple, key of your map is type and values are name, so just loop thru map and insert object in a list e.g.
var d = { "fruit" : ["mango","orange"],"veg" :["carrot"]}
var l = []
for(var type in d){
l.push({'type':type, 'name': d[type]})
}
console.log(l)
output:
[{"type":"fruit","name":["mango","orange"]},{"type":"veg","name":["carrot"]}]
Not exactly the answer you are looking for, but it could be useful for general purpose.
var hash2Array = function(hash, valueOnly) {
return Object.keys(hash).map(function(k) {
if (valueOnly) {
return hash[k];
} else {
var obj={};
obj[k] = hash[k];
return obj;
}
});
};
//output
hash2Array({a:1, b:2}); // [{a:1}, {b:2}]
hash2Array({a:1, b:2},true) // [1,2]
In case of using underscore.js:
var original = {
"fruit" : ["mango","orange"],
"veg" : ["carrot"]
}
var converted = _.map(original, function(name, type){
return {
type: type,
name: name
};
});
No Need of loop
var a = {
"fruit" : ["mango","orange"],
"veg" : ["carrot"]
};
var b = [
{ "type" : "fruit" , "pop" : function(){this.name = a[this.type]; delete this.pop; return this} }.pop() ,
{ "type" : "veg" , "pop" : function(){this.name = a[this.type]; delete this.pop; return this} }.pop()
]