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.
Related
I have this json I need to format it in Typescript or java script. what would be better way to do.
var data = {
"value 1" : [
{
type : String,
Dicription : "abc"
},
{
type : int,
Dicription : "xyz"
},
{
type : String,
Dicription : "pqr"
},
]
"value 2" : [
{
type : String,
Dicription : "abc"
}
]
"value 3" : [
{
type : String,
Dicription : "abc"
},
{
type : int,
Dicription : "xyz"
}
}
Need Output like this
{
{
value : value1,
type : String,
Description : "abc"
},
{
value : value1,
type : int,
Dicription : "xyz"
},
{
value : value1,
type : String,
Dicription : "pqr"
},
{
value : value2,
type : String,
Description : "abc"
},
{
value : value3,
type : String,
Description : "abc"
},
{ value : value3,
type : int,
Description : "xyz"
}
}
I tried
var new = [];
Var values = Object.keys(data)
values.ForEach(Function(value){
new.push({
'value' : value })
});
and iterate it, but could not get desired output. I tried to flatten this but I got objects like {value : value , { type: String ,Description : abc}}
What should I do to solve it
Convert the object to an an array using Object.entries(), and then flat map the entries to an array of objects:
const result = Object.entries(data) // get the entries of the object
.flatMap(([value, arr]) => // map and flatten the sub-arrays
arr.map(o => ({ // map each sub-array and combine with the value
value,
...o
}))
)
I get the keys and values separately and iterate though first object and create another object and push values in it.
So my solution for the problem is
var keys = Object.keys(data);
var values = Object.values(data);
var length = Object.values(data).length;
keys.ForEach(function (key, index){
values.ForEach(function (obj, i) {
if(index == i){
var innerValue = Object.values(obj);
for(i=0 ; i<=innerValue.length; i++)
{
new.push({
'value: key,
'type': innerValue[i].type,
'Description': innerValue[i].abc,
});
}
}
});
});```
I have object like this :
let data = { name : "Me" , age : "20" }
I want to change object to be like this :
data = { age : "20" , name : "Me" }
That's a super strange thing to want to do but:
function reverse(data) {
return Object.entries(data).reduce((reverse, entry) => {
reverse[entry[1]] = entry[0];
return reverse;
}, {})
}
...will swap the keys and values directly within the provided object.
data = { name : "Me" , age : "20" }
reverse(data)
// {20: "age", Me: "name"}
I am having a complex JSON object which I want to compare like below :
$scope.new = [
{
"name": "Node-1",
"isParent": true,
"text" : [
{
"str" : "This is my first Node-1 string",
"parent":[]
},
{
"str" : "This is my second Node-1 string",
"parent":[]
}],
"nodes": [
{
"name": "Node-1-1",
"isParent": false,
"text" : [
{
"str" : "This is my first Node-1-1 string",
"parent":[]
},
{
"str" : "This is my second Node-1-1 string",
"parent":[]
}],
"nodes": [
{
"name": "Node-1-1-1",
"isParent": false,
"text" : [
{
"str" : "This is my first Node-1-1-1 string",
"parent":[]
},
{
"str" : "This is my second Node-1-1-1 string",
"parent":[]
}],
"nodes": []
}
]
}
]
}
]
But while comparing I want to ignore 1 property also but as I am using Angular.js I don't see any option in angular.equal which will omit that property while comparing 2 object.
console.log(angular.equals($scope.new,$scope.copy));
So while doing research I came with below answer which is using lodash having emit option but problem is I guess omit create a copy and I guess I will have performance degradation in case of lodash.
Exclude some properties in comparison using isEqual() of lodash
So now I am thinking to convert object so string and then do comparison and I guess that will be fast but problem is how I will omit that property while string comparison?
Something like this:
var str1 = JSON.stringify(JSON.stringify($scope.new));
var str2 = JSON.stringify(JSON.stringify($scope.copy));
console.log(str1==str2);
Note: I want to ignore isParent property while comparing 2 object.
What would be the best way to do compare 2 object?
Converting to strings is not the best approach in these cases.
Keep them as objects.
Using loadash:
const propertiesToExclude = ['isParent'];
let result = _.isEqual(
_.omit(obj1, propertiesToExclude),
_.omit(obj2, propertiesToExclude)
);
Using plain AngularJS, create a copy of the objects removing the not needed properties and then compare them:
let firstObj = angular.copy(obj1);
let secondObj = angular.copy(obj2);
const propertiesToExclude = ['isParent'];
function removeNotComparatedProperties(obj) {
propertiesToExclude.forEach(prop => {
delete obj[prop];
});
}
removeNotComparatedProperties(firstObj);
removeNotComparatedProperties(secondObj);
angular.equals(firstObj, secondObj);
You can use lodash and override the standard comparator used for deep comparison if you use _.isEqualWith:
var objA = {
isParent: true,
foo: {
isParent: false,
bar: "foobar"
}
};
var objB = {
isParent: false,
foo: {
isParent: true,
bar: "foobar"
}
};
var comparator = function(left, right, key) {
if (key === 'isParent') return true; // if the key is 'isParent', mark the values equal
else return undefined; // else fall back to the default behavior
}
var isEqual = _.isEqualWith(objA, objB, comparator);
console.log(isEqual); // true
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
To exclude multiple properties, extend the comparator function accordingly:
var comparator = function(left, right, key) {
if (key === 'isParent' || key === 'anotherKey') return true;
else return undefined;
}
You could also use a number of different approaches syntactically, depending on what you prefer -- a switch statement, an array that you iterate...
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 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()
]