Remove numbered keys from object recursively - javascript

I have the following JSON.
var a = {
"0": "123",
"1": 'gio',
"again": "blabla",
"ah" : "fff",
"ss" : {
"0": "444",
"1": "555",
"final": "letssee"
}
}
I want to get the following one
var a = {
"again": "blabla",
"ah" : "fff",
"ss" : {
"final": "letssee"
}
}
The idea is that we recursively go through the whole object and remove all the keys that are numbers. Object could go deeper and deeper, so that's why we need recursion.
I tried to come up with a code, but somehow my solution is huge, and very ugly.
I'd appreciate a ES6 way(clean and easy)...

const isObj = (x) => x !== null && typeof x === "object" && !Array.isArray(x);
const removeNumericKeys = (o) =>
Object.fromEntries(
Object.entries(o).flatMap(([k, v]) =>
isNaN(Number(k)) ? [[k, isObj(v) ? removeNumericKeys(v) : v]] : []
)
);

basically you can loop using for ... in using recursion and check for the keys of the object using parseInt with isNaN to check if it is numeric or not.
this is a destructive way to achive it, if you look, it will change the principal object.
let obj = {
"0": "123",
"1": 'gio',
"again": "blabla",
"ah": "fff",
"ss": {
"0": "444",
"1": "555",
"final": "letssee"
}
}
function deleteRecursive(obj) {
for (let k in obj) {
const parsedKey = parseInt(k);
// if it is NOT NAN it means it is numeric, so we delete it
if (!isNaN(parsedKey)) {
delete obj[k];
} else if (typeof obj[k] == "object" && obj[k] !== null) {
deleteRecursive(obj[k]);
}
}
}
deleteRecursive(obj);
console.log(obj)
this is another way to do it if you want to keep the old object and assign it to a new one.
let obj = {
"0": "123",
"1": 'gio',
"again": "blabla",
"ah": "fff",
"ss": {
"0": "444",
"1": "555",
"final": "letssee"
}
}
let newObj = {}
function deleteRecursive(obj, newObj) {
for (let k in obj) {
const parsedKey = parseInt(k);
// if it is NOT NAN it means it is numeric, so we dont add it
if (typeof obj[k] == "object" && obj[k] !== null) {
newObj[k] = {}
deleteRecursive(obj[k], newObj[k]);
} else if (isNaN(parsedKey)) {
newObj[k] = obj[k]
}
}
}
deleteRecursive(obj, newObj);
console.log(newObj)

Related

check if keys exist in object and if it has value

How do we check the keys and compare it to the data object , if one or more keys from the keys array does not exist in object data or if it exist or key exists and the key value has no value or null or undefined then return false else return true.
For example keys has a key summary and it exists on object data but the value is empty so it should return false;
I've tried Object.keys and used includes but cant seem to work it out, maybe someone has an idea. Thanks.
#currentCode
const sample = Object.entries(sampleOject).some((value) => {
return keys.includes(value[0]) ? false : (value[1] === null || value[1] === "");
})
Thanks.
#keys
const keys = [
'summary',
'targetRecdate',
'majorPositiveAttributes',
'generalRealEstateConcernsorChallenges',
'terminationPayment',
'effectiveDate',
'brokerCommission',
'brokerRebate',
'netEffectiveBrokerCommission']
#sample object data
{
"dealDispositionType": "A",
"majorPositiveAttributes": "a",
"terminationPayment": "31",
"netEffectiveBrokerCommission": -12189,
"brokerCommission": "123",
"brokerRebate": "12312",
"isPharmacyRestriction": 0,
"periodOfRestriction": null,
"pharmacyRestrictionDate": null,
"targetRecdate": "2022-10-20",
"isLandLordConsent": false,
"summary: ""
}
You could use every() with hasOwnProperty and additional checks for empty strings etc
const result = keys.every(key => {
return data.hasOwnProperty(key) && data[key] !== ''
}, {});
const keys = [
'summary',
'targetRecdate',
'majorPositiveAttributes',
'generalRealEstateConcernsorChallenges',
'terminationPayment',
'effectiveDate',
'brokerCommission',
'brokerRebate',
'netEffectiveBrokerCommission'
];
const data = {
"dealDispositionType": "A",
"majorPositiveAttributes": "a",
"terminationPayment": "31",
"netEffectiveBrokerCommission": -12189,
"brokerCommission": "123",
"brokerRebate": "12312",
"isPharmacyRestriction": 0,
"periodOfRestriction": null,
"pharmacyRestrictionDate": null,
"targetRecdate": "2022-10-20",
"isLandLordConsent": false,
"summary": ""
};
const result = keys.every(key => {
return data.hasOwnProperty(key) && data[key] !== ''
}, {});
console.log(result); // False
I just optimized your code.
const sample = Object.entries(sampleOject).map(([key, value]) => {
return keys.includes(key) ? value ? true : false : false;
})
...
const keys = [
'summary',
'targetRecdate',
'majorPositiveAttributes',
'generalRealEstateConcernsorChallenges',
'terminationPayment',
'effectiveDate',
'brokerCommission',
'brokerRebate',
'netEffectiveBrokerCommission']
const obj = {
"dealDispositionType": "A",
"majorPositiveAttributes": "a",
"terminationPayment": "31",
"netEffectiveBrokerCommission": -12189,
"brokerCommission": "123",
"brokerRebate": "12312",
"isPharmacyRestriction": 0,
"periodOfRestriction": null,
"pharmacyRestrictionDate": null,
"targetRecdate": "2022-10-20",
"isLandLordConsent": false,
"summary": "test"
}
let arr = [];
const result = Object.entries(obj).map(([key, val]) => {
if (keys.includes(key)) {
if ((val !== '') && (val !== 'undefined') && (val !== 'null') ) {
return true;
} else {
return false;
}
} else {
return false;
}
})
const getValue = result.includes(true);
console.log(getValue)
My approach would be to check whether all keys are present in data with the help of .every.
Also non-strict != will check if certain key contain neither null nor undefined
const keys = [
'summary',
'targetRecdate',
'majorPositiveAttributes',
'generalRealEstateConcernsorChallenges',
'terminationPayment',
'effectiveDate',
'brokerCommission',
'brokerRebate',
'netEffectiveBrokerCommission'];
const data = {
"dealDispositionType": "A",
"majorPositiveAttributes": "a",
"terminationPayment": "31",
"netEffectiveBrokerCommission": -12189,
"brokerCommission": "123",
"brokerRebate": "12312",
"isPharmacyRestriction": 0,
"periodOfRestriction": null,
"pharmacyRestrictionDate": null,
"targetRecdate": "2022-10-20",
"isLandLordConsent": false,
"summary": ""
};
const check = (obj, keys) => keys.every((key) =>
key in obj && obj[key] != undefined);
console.log(check(data, keys));
According to mdn,
const car = { make: 'Honda', model: 'Accord', year: 1998 };
console.log('make' in car); // output: true

Cannot convert undefined or null to object when iterating through valid JSON

I have valid JSON with this structure
const myJSONExample = {
"SubItems": [
{
"SubItems": [
{
"ItemNo": "000001"
}
],
"ItemNo": null,
"Number": null,
"price": 114.46
},
{
"SubItems": [
{
"Group": "0.4.004"
}
],
"type": null
},
{
"SubItems": [
{
"ItemNo": "000005"
},
{
"Quantity": 2
}
],
"Material": "Steel"
},
{
"Description": null
}
]
}
and just simply trying to format all number types in it, using recursive iteration.
const iterate = (obj) => {
Object.keys(obj).forEach(key => {
if(typeof(item[key]) == "number"){
item[key] = new Intl.NumberFormat("de-DE").format(item[key]) //format number for german lang.
}
if (typeof obj[key] === 'object') {
iterate(obj[key])
}
})
}
iterate(myJSONExample);
I used this functions on other JSONs, and been trying to understand for some time, why this throws TypeError: Cannot convert undefined or null to object
null is an "object" hence your issue. So add a truthy check
const iterate = (obj) => {
Object.keys(obj).forEach(key => {
const value = obj[key]
const valueType = typeof value
if (valueType === "number") {
obj[key] = new Intl.NumberFormat("de-DE").format(value)
} else if (valueType === 'object' && value) {
iterate(value)
}
})
}

Why lodash converts my array into object?

I am new to lodash, and created a function that removes the key from the object where a value is null or blank.
But when i am passing my object which contains some part as array it removes array and converts it into an object.
Below is my code which i tried:
_.mixin({ 'removeFalsies': this.removeFalsies });
_.mixin({
removeEmptyObjects: this.removeEmptyObjects
});
removeFalsies (obj) {
return _.transform(obj, function (o, v, k) {
if (v && typeof v === 'object') {
if (v !== '') {
o[k] = _.removeFalsies(v);
}
} else if (v === false) {
o[k] = v;
} else if (v) {
o[k] = v;
}
});
}
removeEmptyObjects (obj) {
return _(obj)
.pickBy(_.isObject)
.mapValues(_.removeEmptyObjects)
.omitBy(_.isEmpty)
.assign(_.omitBy(obj, _.isObject))
.value();
}
Below is the JSON which i am providing to omit blank and null value, so it should remove all the properties from "smallMap" object as series, align are blank object and should remove height as well.
On the other hand, it should remove "mid" from heatrules as well as series1 from children array.
var finalProp = {
"smallMap": {
"series": {},
"align": {},
"height": ""
},
"series": [
{
"heatRules": [
{
"min": "#555",
"mid": null
}
],
"mapPolygons": {
"tooltipText": "{name}",
"togglable": true
},
"id": "value"
}
],
"children": [
{
"type": "HeatLegend",
"width": "100%",
"series1": null
}
]
}
_.removeEmptyObjects(_.removeFalsies(finalProp));
It is removing everything as expected but only 1 issue it is converting array to object.
It is providing below incorrect output instead of series: {0 : {}} it should provide series: [{}] and instead of children: {0 :{}} it should provide [{}].
{
"series": {
"0": {
"heatRules": {
"0": {
"min": "#555"
}
},
"mapPolygons": {
"tooltipText": "{name}",
"togglable": true
},
"id": "value"
}
},
"children": {
"0": {
"type": "HeatLegend",
"width": "100%"
}
}
}
i am not able to find where is the issue any help is appreciated.
main two problems one which was mention by #Akrion for that you can do something as below:
removeFalsies (obj) {
return _.transform(obj, function (o, v, k, l) {
// here you have to check for array also as typeof will return object for array also
if (v && _.isObject(v) && !_.isArray(v)) {
if (v !== '' && !_.omitBy(v, _.isObject)) {
o[k] = removeFalsies(v);
}
} else if(_.isArray(v)) {
if(!o.hasOwnProperty(k)) o[k] = [];
//And if it is array loop through it
_.forEach(v, function(v1, k1) {
o[k].push(removeFalsies(v1));
});
} else if (v === false) {
o[k] = v;
} else if (v) {
o[k] = v;
}
});
}
while calling function just try to remove mapValues as it create object and convert array in objects because of that your key 0 is getting converted to key
removeEmptyObjects =function (obj) {
return _(obj).pickBy(_.isObject).omitBy(_.isEmpty).assign(_.omitBy(obj, _.isObject)).value();
}
Hope this will help you, not tested properly yes just short description.
You issue is in your function removeFalsies where you do not account properly for arrays. Remember Arrays are also objects and your check for if (v && typeof v === 'object') is not sufficient.
What happens is that you do:
if (v && typeof v === 'object') {
if (v !== '') {
o[k] = _.removeFalsies(v);
}
}
Where when you pass an array in that _.transform the k is 0 and you end up creating an object with key 0 which value is then _.removeFalsies(v);
Just put a breakpoint or debugger in that if and you can easily see the issue.
Also note that lodash already has plenty of methods to check for objects like _.isObject as well as arrays _.isArray ... _.isString & _.isEmpty etc.
If your code uses
typeof v === 'object'
it will return true for arrays.
To check for array, use
Array.isArray(t)
Treating array as object and iterating over keys will result in your issue.
Sample Function To Recurse But Not Process Arrays
function removeFalsies(obj) {
return _.transform(obj, function(o, v, k, l) {
if (Array.isArray(obj) {
for (let arrItem of obj) {
removeFalsies(arrItem);
}
return
}
// else not array...
})
}

How can I remove blank fields from a json?

So I am given a json and I have to clean it up, by removing all the 'blank' fields. The fields that are considered blank are:
empty arrays or objects,
strings with whitespace ("" or " "),
null values.
This is what I have so far:
const isBlank = (val) => {
if(val === null ) {
return true
}
if(typeof val === 'string'){
return val.trim().length === 0;
}
return val.length === 0
};
function clean(jsonInput) {
Object.keys(jsonInput).forEach( key => {
if(typeof jsonInput[key] === 'object' && !isEmpty(jsonInput[key])){
clean(jsonInput[key])
}else {
isEmpty(jsonInput[key_field]) && delete jsonInput[key]
}
})
}
This is the jsonInput I am working with:
{
"print": "notBlank",
"this": "notBlank",
"example": "notBlank",
"blank": null,
"allBlanks": [
{
"from": "",
"blank0": null
}
],
"att2": {
"blank1": "",
"blank2": []
},
"att3": {
"one": "1",
"two": "2",
"three": "3",
"blank3": " "
}
}
This should be the output:
{
"print": "notBlank",
"this": "notBlank",
"example": "notBlank",
"att3": {
"one": "1",
"two": "2",
"three": "3"
}
}
Instead I am getting this:
{
"print": "notBlank",
"this": "notBlank",
"example": "notBlank",
"allBlanks": [{ }],
"att2": {},
"att3": {
"one": "1",
"two": "2",
"three": "3",
}
}
It seams like I am unable to remove the objects... Any ideas what I am doing wrong? Any way I can fix it.
Also is there a way to do this so that I don't change the original object and instead I make a duplicate, maybe with map or filter?
The main issue here is that [{}] was not defined as "empty" because it was an array of length 1 with an object in it. However, because you would like empty objects to be considered empty, and thus arrays with empty objects to be empty, you need to also recurse inside of your isEmpty function to cover these angles.
Note the two recursive calls for arrays and objects added to isEmpty.
As for as copying goes, the quick dirty way would be to first stringify then parse the json. You can see this towards the bottom of the code with the line
var jsonCopy = JSON.parse(JSON.stringify(json));
There are more complex ways of deep copying as well, please read What is the most efficient way to deep clone an object in JavaScript? for more information there.
const isEmpty = (val) => {
if(val === null ) {
return true
}
if(typeof val === 'string'){
return val.trim().length === 0;
}
if(val instanceof Array){
if( val.length === 0 ) return true;
return val.every( v =>
isEmpty(v)
);
}
if(val === Object(val)){
if(Object.keys(val).length == 0) return true;
return Object.values(val).every(
v => isEmpty(v)
);
}
return val.length === 0;
};
function clean(jsonInput) {
Object.keys(jsonInput).forEach( key => {
if(typeof jsonInput[key] === 'object' && !isEmpty(jsonInput[key])){
clean(jsonInput[key])
}else {
isEmpty(jsonInput[key]) && delete jsonInput[key]
}
})
}
var json = {
"print": "notBlank",
"this": "notBlank",
"example": "notBlank",
"blank": null,
"allBlanks": [
{
"from": "",
"blank0": null
}
],
"att2": {
"blank1": "",
"blank2": []
},
"att3": {
"one": "1",
"two": "2",
"three": "3",
"blank3": " "
}
};
var jsonCopy = JSON.parse(JSON.stringify(json));
clean(jsonCopy);
console.log(jsonCopy);
Some features like this already exist in the lodash
https://lodash.com/docs/4.17.10#omitBy
Avoid using delete in javascript, its slow and not a good pratice
Example:
var _ = require("lodash")
var oldObj = {a:1, b:2, c:null, d:"aa"}
var newObj = _.omitBy(oldObj, (value, key) =>
_.isNull(value) ||
(_.isString(value) && _.isEmpty(value)) ||
(_.isArray(value) && _.isEmpty(value)) );
console.log("Final", newObj) //Final { a: 1, b: 2, d: 'aa' }
isEmpty return true if value is a number https://lodash.com/docs/4.17.10#isEmpty
Edit:
The keyword delete throws an exception in strict mode https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete

How to access json data property in nodejs

I have a json that is similar to the code below.
var jsonData = {
"Config": {
"AttachStderr": false,
"AttachStdin": false,
"AttachStdout": false,
"CpuShares": 0,
"Cpuset": "",
"Domainname": "",
"Entrypoint": null,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HOME=/root"
]
"Hostname": "git",
"WorkingDir": ""
},
"Created": "2015-03-03T08:59:05.735601013Z",
"Name": "/git",
"NetworkSettings": {
"Ports": {
"22/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "2008"
}
],
"80/tcp": null,
"8006/tcp": [
{
"HostIp": "0.0.0.0",
"HostPort": "9008"
}
]
}
},
"ResolvConfPath": "/etc/resolv.conf",
"State": {
"Pid": 6146,
"Running": true,
"StartedAt": "2015-03-03T08:59:05.829535361Z"
}
}
As you can see any properties has it's own unique name. My question is: How can I access the properties like Env in this way getValue(jsonData, 'Env'); ?
My json is much bigger and more complex than what I put above.
Here's a function that allows you to recurse through an object to find the information you need if you don't know the structure of the object. Note that this will break out of the function when the first instance of that key has been found. This means that if you have more than one key called HostIp, it will only find the first one.
function getValue(obj, key) {
var found = null;
var recurse = function (obj, key) {
for (var p in obj) {
if (p === key) {
found = obj[p];
break;
}
if (obj[p] !== null && typeof obj[p] === 'object') recurse(obj[p], key);
}
}
recurse(obj, key);
return found;
}
getValue(jsonData, 'Env'); // [ "PATH=/usr/local/sbin:/usr/local/bin…", "HOME=/root" ]
If you want to find all instances of a particular key name use the following code instead. It will add all matches to an array and return that array once the object has been trawled. This isn't particularly useful tho because it doesn't provide the context in which it found the key, but it might give you a few ideas.
function getValue(obj, key) {
var found = [];
var recurse = function (obj, key) {
for (var p in obj) {
if (p === key) {
found.push(obj[p]);
}
if (obj[p] !== null && typeof obj[p] === 'object') recurse(obj[p], key);
}
}
recurse(obj, key);
return found;
}
getValue(jsonData, 'HostPort'); // [ "2008", "9008" ]
DEMO

Categories