Remove all undefined from object of array properties - javascript

I'm trying to remove all undefined field value from the following object.
Is there any way to remove all undefined value and get clear object(It means object without any undefined value) without recursive function?
I have tried with lodash something like this
_.transform(obj, function(res, v, k) {
if (v) res[k] = v;
});
and
all I can get succeed object was by doing recursively something like this.
function compactObject(data) {
return Object.keys(data).reduce(function(accumulator, key) {
const isObject = typeof data[key] === 'object';
const value = isObject ? compactObject(data[key]) : data[key];
const isEmptyObject = isObject && !Object.keys(value).length;
if (value === undefined || isEmptyObject) {
return accumulator;
}
return Object.assign(accumulator, {[key]: value});
}, {});
}
However, I would like to make it more simplify. Can any one has good idea of this?
Problematic object
var fields = {
name: "my name",
note: "my note",
steps: [
{action: 'pick', place: {…}, childrenIds: ['_id1', '_id2']},
{action: 'drop', place: {…}, childrenIds: undefined},
],
email: undefined
}
Wanted result
var fields = {
name: "my name",
note: "my note",
steps: [
{action: 'pick', place: {…}, childrenIds: ['_id1', '_id2']},
{action: 'drop', place: {…}},
],
}
Thank you in advance!

const data = {
name: "my name",
note: "my note",
steps: [
{action: 'pick', place: {}, childrenIds: ['_id1', '_id2']},
{action: 'drop', place: {}, childrenIds: undefined},
],
email: undefined
}
function removeUndefined(o) {
let stack = [o], i;
while(stack.length) {
Object.entries(i=stack.pop()).forEach(([k,v])=>{
if(v===undefined) delete i[k];
if(v instanceof Object) stack.push(v);
})
}
return o;
}
console.log(removeUndefined(data))

you can try this function
const removeUndefined = obj => {
if (Array.isArray(obj)) {
return obj.map(item => removeUndefined(item)).filter(item => Object.keys(item).length > 0);
} else if (typeof obj === 'object' && obj !== null) {
return Object.fromEntries(
Object.entries(obj)
.filter(([, value]) => typeof value !== 'undefined')
.map(([key, value]) => {
if (typeof value === 'object' && value !== null) {
return [key, removeUndefined(value)];
}
return [key, value];
})
);
}
return obj;
};
const result = removeUndefined(fields);

Related

Separating (n) keys from array of objects into a single array with keys names

I need to perform filter in the array of objects to get all the keys. Although, whenever there is a obj inside of that key, I would need to get the key name and concat with the key name from the obj, so for example:
const data = [ id: 5, name: "Something", obj: { lower: True, higher: False } ]
result = ["id", "name", "obj.lower", "obj.higher"]
I could manage to do the above code, but, if there is more objs inside the data, I would need to keep adding a if condition inside of my logic, I would like to know if there is any other way, so it doesn't matter how many objects I have inside the objects, It will concat always.
The code I used from the above mention:
const itemsArray = [
{ id: 1, item: "Item 001", obj: { name: 'Nilton001', message: "Free001", obj2: { test: "test001" } } },
{ id: 2, item: "Item 002", obj: { name: 'Nilton002', message: "Free002", obj2: { test: "test002" } } },
{ id: 3, item: "Item 003", obj: { name: 'Nilton003', message: "Free003", obj2: { test: "test003" } } },
];
const csvData = [
Object.keys(itemsArray[0]),
...itemsArray.map(item => Object.values(item))
].map(e => e.join(",")).join("\n")
// Separating keys
let keys = []
const allKeys = Object.entries(itemsArray[0]);
for (const data of allKeys) {
if (typeof data[1] === "object") {
const gettingObjKeys = Object.keys(data[1]);
const concatingKeys = gettingObjKeys.map((key) => data[0] + "." + key);
keys.push(concatingKeys);
} else {
keys.push(data[0])
}
}
//Flating
const flattingKeys = keys.reduce((acc, val: any) => acc.concat(val), []);
What I would like to achieve, lets suppose I have this array of object:
const data =
[
{ id: 10, obj: {name: "Name1", obj2: {name2: "Name2", test: "Test"}}}
...
]
Final result = ["id", "obj.name", "obj.obj2.name2", "obj.obj2.test"]
OBS: The first obj contains all the keys I need, no need to loop through other to get KEYS.
I would like to achieve, all the keys from the first object of the array, and if there is objects inside of objects, I would like to concat the obj names (obj.obj2key1)
You could map the key or the keys of the nested objects.
const
getKeys = object => Object
.entries(object)
.flatMap(([k, v]) => v && typeof v === 'object'
? getKeys(v).map(s => `${k}.${s}`)
: k
),
getValues = object => Object
.entries(object)
.flatMap(([k, v]) => v && typeof v === 'object'
? getValues(v)
: v
),
data = { id: 1, item: "Item 001", obj: { name: 'Nilton001', message: "Free001", obj2: { test: "test001" } } },
keys = getKeys(data),
values = getValues(data);
console.log(keys);
console.log(values);
.as-console-wrapper { max-height: 100% !important; top: 0; }
something like this
const itemsArray = [
{ id: 1, item: "Item 001", obj: { name: 'Nilton001', message: "Free001", obj2: { test: "test001" } } },
{ id: 2, item: "Item 002", obj: { name: 'Nilton002', message: "Free002", obj2: { test: "test002" } } },
{ id: 3, item: "Item 003", obj: { name: 'Nilton003', message: "Free003", obj2: { test: "test003" } } },
];
const item = itemsArray[0];
const getAllKeys = (obj, prefix=[]) => {
if(typeof obj !== 'object'){
return prefix.join('.')
}
return Object.entries(obj).flatMap(([k, v]) => getAllKeys(v, [...prefix, k]))
}
console.log(getAllKeys(item))
The OP solution can be simplified by accepting a prefix param (the parent key) and a results param (defaulted to [] and passed into the recursion) to do the flattening...
let obj = { key0: 'v0', key1: { innerKey0: 'innerV0', innerInner: { deeplyNested: 'v' } }, key2: { anotherInnerKey: 'innerV' } }
function recursiveKeys(prefix, obj, result=[]) {
let keys = Object.keys(obj);
keys.forEach(key => {
if (typeof obj[key] === 'object')
recursiveKeys(key, obj[key], result);
else
result.push(`${prefix}.${key}`)
});
return result;
}
console.log(recursiveKeys('', obj))
function getKeys(obj) {
return Object.keys((typeof obj === 'object' && obj) || {}).reduce((acc, key) => {
if (obj[key] && typeof obj[key] === 'object') {
const keys = getKeys(obj[key]);
keys.forEach((k) => acc.add(`${key}.${k}`));
} else {
acc.add(key);
}
return acc;
}, new Set());
}
// accumulate the keys in a set (the items of the array may
// have different shapes). All of the possible keys will be
// stored in a set
const s = itemsArray.reduce(
(acc, item) => new Set([...acc, ...getKeys(item)]),
new Set()
);
console.log('Keys => ', Array.from(s));
You can use recursion as follows. Since typeof([1,3,5]) is object, we also have to confirm that value is not an array, !Array.isArray(value):
const obj = { id: 10, obj: {name: "Name1", obj2: {name2: "Name2", test: "Test"}}};
const getKeys = (o,p) => Object.entries(o).flatMap(([key,value]) =>
typeof(value) === 'object' && !Array.isArray(value) ?
getKeys(value, (p?`${p}.`:"") + key) :
(p ? `${p}.`: "") + key
);
console.log( getKeys(obj) );

Find and return value if Key exist in JavaScript object which also contain array [duplicate]

This question already has answers here:
foreach return object property javascript
(4 answers)
Closed 1 year ago.
var OBJ = {
code: 42,
item1: [{
id: 1,
name: 'foo'
}, {
id: 2,
city: 'NY'
name: 'bar'
}],
thing: [{
id: 14,
name: 'foo'
}, {
id: 5,
street: 'E43'
name: 'pub'
}]
};
Javascript object(OBJ)
I need a method that returns VALUE of KEY I pass as an argument if KEY is not present in OBJ method should return undefined
getKeyValueFromObject(OBJ , 'street') // should return 'E43'
getKeyValueFromObject(OBJ , 'dog') // should return undefined
I tried this(not working)
getKeyValueFromObject(obj: any, search: string) {
const notFound = {};
Object.keys(obj).forEach(key => {
if (key !== null && key !== undefined && !(obj[key] === undefined || obj[key] === null)) {
if (key === search) {
return obj[key];
} else if (obj[key].constructor === {}.constructor) {
const result = this.getKeyValueFromObject(obj[key], search);
if (result !== notFound) return result;
} else if (Array.isArray(obj[key])) {
obj[key].forEach(element => {
const result = this.getKeyValueFromObject(element, search);
if (result !== notFound) return result;
});
}
}
});
return {};
}
Your code was almost working, but the return statements in your forEach code were not working as you expected. Look here: Grab the return value and get out of forEach in JavaScript?. Instead I used a variable resultKey to store a match.
There were some commas missing in your OBJ json, but I think that was not the main problem.
var OBJ = {
code: 42,
item1: [{
id: 1,
name: 'foo'
}, {
id: 2,
city: 'NY',
name: 'bar'
}],
thing: [{
id: 14,
name: "foo"
}, {
id: 5,
street: "E43",
name: "pub"
}]
};
function getKeyValueFromObject(obj, search) {
let resultKey = undefined;
const notFound = {};
Object.keys(obj).forEach(key => {
if (key !== null && key !== undefined && !(obj[key] === undefined || obj[key] === null)) {
if (key === search) {
resultKey = obj[key];
} else if (obj[key].constructor === {}.constructor) {
const result = this.getKeyValueFromObject(obj[key], search);
if (result !== notFound) resultKey = result;
} else if (Array.isArray(obj[key])) {
obj[key].forEach(element => {
const result = this.getKeyValueFromObject(element, search);
if (result !== notFound) resultKey = result;
});
}
}
return;
});
return resultKey;
}
console.log(getKeyValueFromObject(OBJ, "street"));
console.log(getKeyValueFromObject(OBJ , "dog"));

Modify key and values of an Array of Object and copy it into another Array of Object

I have an array of objects. I have to copy the values into another object with modified keys and values. What I mean by modified is as follows:
type key shouldn't be copied.
_applicant/_spouse and has to be removed from key
values true has to be converted to "Y" and false has to be converted to "N"
Source Obj:
MemberObj:[{
type: 'applicant',
dateOfBirth_applicant: '10-10-1980',
seekingCoverage_applicant: false
relationshipToPrimary: 'SELF'
},
{
type: 'spouse',
dateOfBirth_spouse: '10-10-1990',
seekingCoverage_spouse: true
relationshipToPrimary: 'DEPENDANT'
}];
Destination Object:
prepareMemberData: [
{
dateOfBirth: '10-10-1980',
seekingCoverage: "N",
relationshipToPrimary: 'SELF'
},
{
dateOfBirth_spouse: '10-10-1990',
seekingCoverage: "Y",
relationshipToPrimary: 'DEPENDANT'
}];
Code:
let prepareMemberData = [];
if (MemberObj.length){
prepareMemberData = MemberObj.map(obj => {
console.log('obj', obj, typeof obj);
return Object.entries(obj).forEach(([key, value]) => {
//Not sure how to proceed. I tried Object.assign. But didn't get the expected results
});
})
}
You could first map over each object and then use reduce on Object.entries to modify each individual object.
const data = [{
type: 'applicant',
dateOfBirth_applicant: '10-10-1980',
seekingCoverage_applicant: false,
relationshipToPrimary: 'SELF'
},
{
type: 'spouse',
dateOfBirth_spouse: '10-10-1990',
seekingCoverage_spouse: true,
relationshipToPrimary: 'DEPENDANT'
}
]
const result = data.map(({ type, ...rest }) => {
return Object.entries(rest).reduce((r, [key, value]) => {
['_spouse', '_applicant'].some(e => {
if (key.includes(e)) key = key.replace(e, '')
})
if (typeof value === 'boolean') {
value = value ? 'Y' : 'N'
}
r[key] = value;
return r;
}, {})
})
console.log(result)
You can create new object in the map function and return it. Inside map function you can transform it to whatever your requirements are.
const MemberObj = [{
type: 'applicant',
dateOfBirth_applicant: '10-10-1980',
seekingCoverage_applicant: false,
relationshipToPrimary: 'SELF'
},
{
type: 'spouse',
dateOfBirth_spouse: '10-10-1990',
seekingCoverage_spouse: true,
relationshipToPrimary: 'DEPENDANT'
}];
let prepareMemberData = [];
if (MemberObj.length){
prepareMemberData = MemberObj.map(obj => {
console.log('obj', obj, typeof obj);
const dateOfBirth = obj['dateOfBirth_spouse'] ? obj['dateOfBirth_spouse'] : obj['dateOfBirth_applicant'];
const seekingCoverage = obj['seekingCoverage_spouse'] ? obj['seekingCoverage_spouse'] : obj['seekingCoverage_applicant'];
const newObj = {
dateOfBirth: dateOfBirth,
seekingCoverage: seekingCoverage === true ? 'Y' : 'N',
relationshipToPrimary: obj['relationshipToPrimary']
};
return newObj;
});
}
console.log("member obj", MemberObj);
console.log('members obj transformed', prepareMemberData);

How to flatten an object with nested objects in javascript

I have some attributes from a nested object that is inside the parent object but I would like to merge nested object with the parent object to be flatten.
Original object:
enrollment = {
user: {
id: 'string',
name: 'string'
},
finished: 'boolean',
path: 'string'
}
expected flatten object:
user: {
id: 'string',
name: 'string',
finished: 'boolean',
path: 'string'
}
You can recursively build object any number of nested objects. So, this function is not your case dependent:
var enrollment = {
user: {
id: 'string',
name: 'string'
},
finished: 'boolean',
path: 'boolean'
}
var enrollment2 = {
user: {
id: 'string',
name: 'string'
},
test: {
test1: {
test2: {
val0:'val0',
test4: { //3rd level nested object for example
val1: 'val1',
val2: 'val2'
}
}
}
},
finished: 'boolean',
path: 'boolean'
}
const flat = (obj, out) => {
Object.keys(obj).forEach(key => {
if (typeof obj[key] == 'object') {
out = flat(obj[key], out) //recursively call for nesteds
} else {
out[key] = obj[key] //direct assign for values
}
})
return out
}
console.log(flat(enrollment, {}))
console.log(flat(enrollment2, {}))
I needed something that avoids rewriting keys with the same name that were in different levels in the original object. So I wrote the following:
const flattenObject = (obj, parentKey = '') => {
if (parentKey !== '') parentKey += '.';
let flattened = {};
Object.keys(obj).forEach((key) => {
if (typeof obj[key] === 'object' && obj[key] !== null) {
Object.assign(flattened, flattenObject(obj[key], parentKey + key))
} else {
flattened[parentKey + key] = obj[key]
}
})
return flattened;
}
var test = {
foo: 'bar',
some: 'thing',
father: {
son1: 'son1 value',
son2: {
grandchild: 'grandchild value',
duplicatedKey: 'note this is also used in first level',
},
},
duplicatedKey: 'note this is also used inside son2',
}
let flat = flattenObject(test);
console.log(flat);
// how to access the flattened keys:
let a = flat['father.son2.grandchild'];
console.log(a);
Also checks if the object is null, as I was having some problems with that in my usage.
Here's a quick and dirty way to flatten your object:
var enrollment = {
user: {
id: 'string',
name: 'string',
},
fineshed: true,
path: false,
};
var user = Object.assign(enrollment.user);
user.fineshed = enrollment.fineshed;
user.path = enrollment.path;
For a generic method with a couple of caveats of no shared key names and only flattening 1 level of depth:
var enrollment = {
user: {
id: 'string',
name: 'string',
},
fineshed: true,
path: false,
};
const flatten = (object) => {
let value = {};
for (var property in object) {
if (typeof object[property] === 'object') {
for (var p in object[property]) {
value[p] = object[property][p];
}
} else {
value[property] = object[property];
}
}
return value;
};
let user = flatten(enrollment);
console.log(user);
using recursion and reduce.
note that if value itself is an array containing objects, you might want add another check like !Array.isArray(value) depending on your case
function flatObj(obj) {
return Object.entries(obj).reduce(
(flatted, [key, value]) =>
typeof value == "object"
? { ...flatted, ...flatObj(value) }
: { ...flatted, [key]: value },
{}
);
}
Just want a single Object:
const enrollment = {
user: {
id: 'string',
name: 'string'
},
finished: 'boolean',
path: 'boolean'
}
function propsToUser(enrollObj){
const u = {...enrollObj.user};
for(let i in enrollObj){
if(i !== 'user')u[i] = enrollObj[i];
}
return u;
}
const user = propsToUser(enrollment);
console.log(user);
Below code snippet takes nested input object like this :
{
name:'Namig',
surname:'Hajiyev',
address:{
city:'Sumgait',
country:'Azerbaijan',
geo: {
lat:'40.5897200',
long:'49.6686100'
}
}
}
and returns result flattened object like this:
{
"name": "Namig",
"surname": "Hajiyev",
"address.city": "Sumgait",
"address.country": "Azerbaijan",
"address.geo.lat": "40.5897200",
"address.geo.long": "49.6686100"
}
Here is my code :
function flattenObject(obj, newObj, prefix) {
newObj = newObj || {};
prefix = prefix || "";
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
const type = typeof obj[key];
const newKey = !!prefix ? prefix + "." + key : key;
if (type === "string") {
newObj[newKey] = obj[key];
}
else if (type === "object") {
flattenObject(obj[key], newObj, newKey);
}
}
}
return newObj;
}
var obj = {
name:'Namig',
surname:'Hajiyev',
address:{
city:'Sumgait',
country:'Azerbaijan',
geo: {
lat:'40.5897200',
long:'49.6686100'
}
}
}
console.log(flattenObject(obj));

How to remove a property from nested javascript objects any level deep?

Let's say I have nested objects, like:
var obj = {
"items":[
{
"name":"Item 1",
"value": "500",
"options": [{...},{...}]
},
{
"name":"Item 2",
"value": "300",
"options": [{...},{...}]
}
],
"name": "Category",
"options": [{...},{...}]
};
I want to remove the options property from any level deep from all the objects. Objects can be nested within objects, and arrays as well.
We're currently using Lodash in the project, but I'm curious about any solutions.
There is no straight forward way to achieve this, however you can use this below function to remove a key from JSON.
function filterObject(obj, key) {
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] == 'object') {
filterObject(obj[i], key);
} else if (i == key) {
delete obj[key];
}
}
return obj;
}
and use it like
var newObject = filterObject(old_json, "option");
Modifying the above solution, To delete "dataID" which appears multiple times in my JSON . mentioned below code works fine.
var candidate = {
"__dataID__": "Y2FuZGlkYXRlOjkuOTI3NDE5MDExMDU0Mjc2",
"identity": {
"__dataID__": "aWRlbnRpdHk6NjRmcDR2cnhneGE3NGNoZA==",
"name": "Sumanth Suvarnas"
},
};
candidate = removeProp(candidate, "__dataID__")
console.log(JSON.stringify(candidate, undefined, 2));
function removeProp(obj, propToDelete) {
for (var property in obj) {
if (typeof obj[property] == "object") {
delete obj.property
let newJsonData= this.removeProp(obj[property], propToDelete);
obj[property]= newJsonData
} else {
if (property === propToDelete) {
delete obj[property];
}
}
}
return obj
}
A little modification of void's answer that allows for deletion of propertise which are also objects
function filterObject(obj, key) {
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (i == key) {
delete obj[key];
} else if (typeof obj[i] == 'object') {
filterObject(obj[i], key);
}
}
return obj;
}
We now use object-scan for data processing tasks like this. It's very powerful once you wrap your head around it. Here is how you'd answer your questions
// const objectScan = require('object-scan');
const prune = (input) => objectScan(['**.options'], {
rtn: 'count',
filterFn: ({ parent, property }) => {
delete parent[property];
}
})(input);
const obj = { items: [{ name: 'Item 1', value: '500', options: [{}, {}] }, { name: 'Item 2', value: '300', options: [{}, {}] }], name: 'Category', options: [{}, {}] };
console.log(prune(obj));
// => 3
console.log(obj);
// => { items: [ { name: 'Item 1', value: '500' }, { name: 'Item 2', value: '300' } ], name: 'Category' }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan
I had similar issues. So, I developed the following library. Please see the source code of the library on GitHub, and you can download it using npm.
You can use the function removePropertiesDeeply together with isInitialized as below to remove all non-initialized properties (such as an empty object, empty array, empty string, or non-finite number).
const {removePropertiesDeeply, isInitialized} = require("#thedolphinos/utility4js");
const object = {
a: null,
b: "",
x: {
a: null,
b: ""
},
y: [],
z: [
null,
"",
{a: null, b: ""},
[[{a: {b: null, c: ""}}]],
"abc"
]
};
removePropertiesDeeply(object, (x) => !isInitialized(x));
console.log(JSON.stringify(object)); // See that the object becomes {"z":["abc"]}.
I had a similar issue and I got it resolved. I hope my solution might be helpful to someone.
I use Es6 ... spread operator to do a shallow copy of an object and made null to property I was not interested.
const newObject = {
...obj.items,
...obj.name,
options: null // option property will be null.
}
function omit(source) {
return isArray(source)
? source.map(omit)
: isObject(source)
? (({ options, ...rst }) => mapValues(rst, omit))(source)
: source;
}
as with lodash, that's an easy thing, also you can specify the key via an param like this
function omit(source, omitKey) {
return isArray(source)
? source.map(partialRight(omit,omitKey)))
: isObject(source)
? (({[omitKey]: _, ...rst }) => mapValues(rst, partialRight(omit,omitKey)))(source)
: source;
}
You can remove properties given a condition using following function:
// Warning: this function mutates original object
const removeProperties = (obj, condition = (key, value) => false) => {
for (var key in obj) {
const value = obj[key]
if (!obj.hasOwnProperty(key)) continue
if (typeof obj[key] === "object") {
removeProperties(obj[key], condition)
} else if (condition(key, value)) {
delete obj[key]
}
}
return obj
}
Examples:
// Remove all properties where key is equal to 'options'
removeProperties(someObject, (key, value) => key === 'options'))
// Remove all properties where key starts with 'ignore_'
removeProperties(someObject, (key, value) => key.startsWith('ignore_'))
// Remove all properties where value is null
removeProperties(someObject, (key, value) => value === null))

Categories

Resources