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()
]
Related
I have following object array:
var arr = [
{
id : "a1",
guid : "sdfsfd",
...
value : "abc",
status: "active"
},
{
id : "a2",
guid : "sdfsfd",
...
value : "def",
status: "inactive"
},
{
id : "a2",
guid : "sdfsfd",
...
value : "def"
},
...
]
How to set "status" property of each object to "active". So the resulting array will be:
var arr = [
{
id : "a1",
guid : "sdfsfd",
...
value : "abc",
status: "active"
},
{
id : "a2",
guid : "sdfsfd",
...
value : "def",
status: "active"
},
{
id : "a2",
guid : "sdfsfd",
...
value : "def",
status: "active"
},
...
]
Additionally this should create the property "active" if doesn't exists.
I can do this using for loops. But I'm pretty much sure lodash can do this in one line like:
arr = _.set_property(arr, "status", "active");
You don't need lodash for this.
The first object is missing your status property and it will be added.
SHOWING THREE WAYS HOW YOU CAN DO IT
IMMUTABLE VERSION (We create a new array using map)
const arrImmutableVersion = arr.map(e => ({...e, status: "active"}));
MUTABLE VERSIONS (We change the original array)
arr.forEach((el)=>{el.status = "active";})
or
arr.forEach(function(el){el.status = "active";})
var arr = [
{
id : "a1",
guid : "sdfsfd",
value : "abc"
},
{
id : "a2",
guid : "sdfsfd",
value : "def",
status: "inactive"
},
{
id : "a2",
guid : "sdfsfd",
value : "def",
status: "active"
}
];
// SHOWING THREE WAYS HOW YOU CAN DO IT
// MUTABLE VERSIONS - We change the original array
arr.forEach((el)=>{el.status = "active";}) // ES6
// or
arr.forEach(function(el){el.status = "active";})
//or
// IMMUTABLE VERSION - We create a new array using `map`
const arrImmutableVersion = arr.map(e => ({...e, status: "active"})); // ES6
//--------------------------------------------------------------
// RESULTS:
console.log("logging results of object 'arr'");
console.log(arr);
console.log("---------------------------------------------------------");
console.log("logging results of object 'arrImmutableVersion'");
console.log(arrImmutableVersion);
Indeed, you don't need Lodash, but the question is tagged Lodash, and using Lodash offers some useful defenses that reduces the risk of errors. This solution utilizes _.forEach and _.set
// _.forEach won't throw errors if arr is not an array...
_.forEach(arr, function (obj) {
// _.set won't throw errors if obj is not an object. With more complex objects, if a portion of the path doesn't exist, _.set creates it
_.set(obj, 'status', 'active');
});
If you wanted to make it abstract, you could build a Lodash mixin:
_.mixin({
setProperty: function(arr, key, val) {
_.forEach(arr, function (obj) {
_.set(obj, path, val);
});
}
});
Then, you could use it exactly as you described:
_.setProperty( arr, 'status', 'active' );
A way simpler and and cleaner way !
If you want to use func programming in a proper manner
myArray = myArray.map(arrayElem => {
arrayElem.property = newValue
return arrayElem
})
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
We have MongoDB docs that look like this:
var JavascriptObject = {
DbDocs : [
{
_id : "1",
{..more values..}
},
{
_id : "2",
{..more values..}
},
{
_id : "3",
{..more values..}
}
]
}
Based on certain values in the JavascriptObject, we order an array of the _id from the documents, and the result is this:
var OrderedArray = [ 2, 1, 3 ];
Right now, we're rebuilding the entire JavascriptObject by matching the _id in the OrderedArray with the _id in DbDocs:
var JavascriptObjectToRebuild = [];
var DbDocuments = JavascriptObject.DbDocs;
var DocumentCount = 0;
for (var OrderedNumber in OrderedArray) {
for (var Document in DbDocuments) {
if ( DbDocuments[Document]._id === OrderedArray[OrderedNumber] ) {
JavascriptObjectToRebuild[DocumentCount] = {}; // new Document Object
JavascriptObjectToRebuild[DocumentCount]._id = DbDocuments[Document]._id;
JavascriptObjectToRebuild[DocumentCount]...more values = DbDocuments[Document]...more values;
DocumentCount++; // increment
}
}
}
var SortedJavascriptObject = { DbDocs: [] }; // format for client-side templating
for (var Document in JSONToRebuild) {
SortedJavascriptObject.DbDocs.push(JavascriptObjectToRebuild[Document]);
}
Is there a faster more efficient way to sort the JavascriptObject based on this OrderedArray?
See update below if it's impossible to sort directly and you have to use OrderedArray instead.
If you can apply your criteria within the callback of the Array#sort function (e.g., if you can do it by comparing two entries in the array to one another), you can simply sort JSON.DbDocs directly.
Here's an example that sorts based on the numeric value of _id; naturally you'd replace that with your logic comparing objects.
Also note I've changed the name of the top-level variable (JSON is kinda in use, and in any case, it's not JSON):
var Obj = {
DbDocs : [
{
_id : "2",
more: "two"
},
{
_id : "1",
more: "one"
},
{
_id : "3",
more: "three"
}
]
};
Obj.DbDocs.sort(function(a, b) {
return +a._id - +b._id; // Replace with your logic comparing a and b
});
document.querySelector('pre').innerHTML = JSON.stringify(Obj, null, 2);
<pre></pre>
If it's impossible to sort directly and you have to work from OrderedArray, then it's still possible with sort, but it's less elegant: You use Array#indexOf to find out where each entry in the array should be:
Obj.DbDocs.sort(function(a, b) {
return OrderedArray.indexOf(+a._id) - OrderedArray.indexOf(+b._id);
});
(The + converts the IDs from strings to numbers, since OrderedArray contains numbers in your question, but the ID values are strings.)
Live Example:
var Obj = {
DbDocs : [
{
_id : "1",
more: "one"
},
{
_id : "2",
more: "two"
},
{
_id : "3",
more: "three"
}
]
};
var OrderedArray = [2, 1, 3];
Obj.DbDocs.sort(function(a, b) {
return OrderedArray.indexOf(+a._id) - OrderedArray.indexOf(+b._id);
});
document.querySelector('pre').innerHTML = JSON.stringify(Obj, null, 2);
<pre></pre>
If there are going to be lots of entries in OrderedArray, you might want to make a lookup object first, to avoid lots of indexOf calls (which are costly: (georg did that in an answer, but he's since deleted it for some reason)
var OrderMap = {}
OrderedArray.forEach(function(entry, index) {
OrderMap[entry] = index;
});
Obj.DbDocs.sort(function(a, b) {
return OrderMap[a._id] - OrderMap[b._id];
});
(We don't need to convert the IDs to numbers because property names are always strings, so we've converted the numbers to strings when building the map.)
Live Example:
var Obj = {
DbDocs : [
{
_id : "1",
more: "one"
},
{
_id : "2",
more: "two"
},
{
_id : "3",
more: "three"
}
]
};
var OrderedArray = [2, 1, 3];
var OrderMap = {}
OrderedArray.forEach(function(entry, index) {
OrderMap[entry] = index;
});
Obj.DbDocs.sort(function(a, b) {
return OrderMap[a._id] - OrderMap[b._id];
});
document.querySelector('pre').innerHTML = JSON.stringify(Obj, null, 2);
<pre></pre>
As I understood, you want to get result like so,
[{"_id":"2"}, {"_id":"1"}, {"_id":"3"}]
so you can do it with one forEach and indexOf, like so
var JSONDADA = {
DbDocs : [{_id : "1",}, {_id : "2"}, {_id : "3"}]
};
var DbDocuments = JSONDADA.DbDocs;
var OrderedArray = [ 2, 1, 3 ];
var result = [];
DbDocuments.forEach(function (el) {
var position = OrderedArray.indexOf(+el._id);
if (position >= 0) {
result[position] = el;
}
});
console.log(JSON.stringify(result));
303 see other
......
Convert OrderedNumber into a hash _id => position:
sorter = {}
OrderedNumber.forEach(function(_id, pos) {
sorter[_id] = pos
})
and then sort the target array by comparing id's positions:
DbDocuments.sort(function(a, b) {
return sorter[a._id] - sorter[b._id];
})
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.
What is the best way to filter JSON nested keys and delete them? For example:
{ "id" : "1",
"key1" : "val1",
"key2" : "val2",
"name" : "someone",
"age" : 39,
"data" : [
{ "id" : "1234",
"key1" : "val1",
"key2" : "val2",
"name" : "someone",
"age" : 39
},
{ "id" : "1234",
"key1" : "val1",
"key2" : "val2",
"name" : "someone",
"age" : 39
}
]
}
To get the following JSON by deleting all key1 and key2 items recursively:
{ "id" : "1",
"name" : "someone",
"age" : 39,
"data" : [
{ "id" : "1234",
"name" : "someone",
"age" : 39
},
{ "id" : "1234",
"name" : "someone",
"age" : 39
}
]
}
Thanks.
Something like this should work:
function deleteRecursive(data, key) {
for(var property in data) {
if(data.hasOwnProperty(property)) {
if(property == key) {
delete data[key];
}
else {
if(typeof data[property] === "object") {
deleteRecursive(data[property], key);
}
}
}
}
}
Fiddle here
Assuming this is the JSON for an object called, say, people, something like this should work:
function objWithoutPropsIDontLike(obj, propsIDontLike) {
// check to make sure the given parameter is an object
if(typeof obj == "object" && obj !== null) { // typeof null gives "object" ಠ_ಠ
// for every property name... (see note on Object.keys() and
// Array.forEach() below)
obj.keys().forEach(function(prop) {
// Test if the property name is one of the ones you don't like
// (Array.indexOf() returns -1 if the item isn't found in the array).
if(propsIDontLike.indexOf(prop) >= 0) {
// if it is, nuke it
delete obj[prop];
} else if(obj[prop]) {
// if it isn't, recursively filter it
obj[prop] = filterPropsIDontLike(obj[prop], propsIDontLike);
}
});
}
// There is no else { ... }; if the thing given for "obj" isn't an object
// just return it as-is.
return obj;
}
var propsIDontLike = [ 'key1', 'key2' ];
people = objWithoutPropsIDontLike(people, propsIDontLike);
Note:
Object.keys() and Array.forEach() aren't available in Internet Explorer < 9. Happily MDC provides working polyfills for both: Object.keys(), Array.forEach().
Your question contains your answer: recursively!
Your base cases are the "primitive" JSON types: strings and numbers. These remain unchanged. For arrays, you apply the operation to each element of the array, returning a new array.
The interesting case is objects. Here, for each key-value pair, you apply the operation to each value (but ignore those whose key is one you would like to "delete") and write them into a new object.
As an (off the cuff) example, using jQuery:
var removeKey(object, key){
if(typeof(object)==='number' || typeof(object)==='string'){
return object;
}
else if(typeof(object)==='object'){
var newObject = {};
$.each(object, function(k, value) {
if(k!==key){
newObject[k] = removeKey(value, key);
}
});
return newObject;
}
else {
// Oh dear, that wasn't really JSON!
}
};
If you want to remove more than one key, adjust the second parameter and condition in the recursive case as you see fit.
NOTE This is a non-destructive, which may or may not be what you need; the other answer (by Vivin Paliath) has a destructive version.