Javascript Object get properties from lowest level - javascript

I want to know whats the best way to simplify a object so that it only contains the properties from the lowest level of the actual object. I will explain by the following example:
Starting Object
const start = {
a: 1,
b: {
c: 2,
d: 3
},
e: 4,
f:{
g: 5
}
}
Wanted Object
const wanted = {
a: 1,
c: 2,
d: 3,
e: 4,
g: 5
}
I generally know how to do it, but it seems very lavish for large objects. Thus, I do not want to write:
const simplified = {
a: start.a,
c: start.b.c,
d: start.b.d,
e: start.e,
g: start.f.g
}
Apparently, destructuring an object inside another object is not possible, but it would also help if I can get the children of the parent objects easier, somehow like that:
const simplified = {
a: start.a,
//start.b.children?
e: start.e,
//start.f.children?
}
I also want to avoid to create duplicates of the variables outside the object creation with destructuring.
What is the best practice for restructuring an object in such way?

You can simply create a recursive function that will add property to your result only if the current value is not of the object type, and if it is then pass it to another recursive call.
const start = {"a":1,"b":{"c":2,"d":3},"e":4,"f":{"g":5}}
function flatten(data) {
return Object.entries(data).reduce((r, [k, v]) => {
if (typeof v === 'object') Object.assign(r, flatten(v))
else r[k] = v
return r
}, {})
}
const result = flatten(start)
console.log(result)

You could map either the object or the nested parts.
function flat(object) {
return Object.assign({}, ...Object
.entries(object)
.map(([k, v]) => v && typeof v === 'object'
? flat(v)
: { [k]: v }
));
}
console.log(flat({ a: 1, b: { c: 2, d: 3 }, e: 4, f:{ g: 5 } }));

Related

How to deep remove falsey values and empty objects from an object using lodash

How to deep remove all falsey values and empty objects using lodash ?
f.e. I want my object :
{ a:undefined, b:2, c:4, d:undefined , e:{ f:{} , g:null } }
to become:
{ b:2, c:4 };
var test = {
a: undefined,
b: 2,
c: 4,
d: undefined,
e: {
f: {},
g: null
}
};
function clean(obj) {
for (var propName in obj) {
if (_.isObject(obj[propName])) {
clean(obj[propName]);
}
if (obj[propName] === null || obj[propName] === undefined || _.isObject(obj[propName]) && _.isEmpty(obj[propName])) {
delete obj[propName];
}
}
}
clean(test);
console.log(test);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
You could get the properties to delete and get a clean object. This approach mutates to original object.
function clean(value) {
if (value === null || value === undefined) return true;
if (typeof value !== 'object') return false;
if (Array.isArray(value)) {
let i = value.length;
while (i--) if (clean(value[i])) value.splice(i, 1);
return !value.length;
}
Object
.keys(value)
.filter(k => clean(value[k]))
.forEach(Reflect.deleteProperty.bind(null, value));
return !Object.keys(value).length;
}
const
object = { a: undefined, b: 2, c: 4, d: undefined, e: { f: {}, g: null } , h: [], i: [1, []], j: [[[], []]] };
clean(object);
console.log(object);
The question is pretty vague and doesn't specify whether the original structure may or may not be mutated. There is also nothing in there about arrays.
This answer assumes that you don't want to mutate the original data structure and that "deep" also means to go through all array elements. If the object or array results in an empty instance the corresponding key will be removed.
function deepCompact(collection) {
const add = _.isArray(collection)
? (collection, key, value) => collection.push(value)
: (collection, key, value) => collection[key] = value;
return _.transform(collection, (collection, value, key) => {
if (_.isObject(value)) {
value = deepCompact(value);
if (_.isEmpty(value)) return;
} else {
if (!value) return;
}
add(collection, key, value);
});
}
const data1 = {a: undefined, b: 2, c: 4, d: undefined, e: {f: {} , g: null}};
console.log(deepCompact(data1));
const data2 = [{a: 0, b: 2}, false, {c: [{d: null}]}, {e: [{f: 1}]}];
console.log(deepCompact(data2));
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.20/lodash.min.js"></script>
transform is comparable to reduce, however the first callback parameter is locked to the provided or default accumulator. This means you don't have to return the accumulator from the callback. The default accumulator of transform also differs from reduce. reduce uses the first element as the initial value, whereas transform uses a new object with the same prototype as the provided collection as the default value.

Extend object with deep nested properties [duplicate]

This question already has answers here:
How to deep merge instead of shallow merge?
(47 answers)
Closed 2 years ago.
I have a nested default object that I want to both extend and overwrite with nested properties. I tried with the spread operator but you can also use Ojbect.assign.
The problem is that nested objects get overridden instead of their respective properties getting overridden and/or extended.
const defaultOptions = {
a: 0,
b: 1,
c: {
hello: 'world',
age: 20
}
};
const specialOptions = {
b: 0,
c: {
age: 10
},
d: 1
};
const options = {
...defaultOptions,
...specialOptions
}
console.log(options)
const expectedOptions = {
a: 0,
b: 0,
c: {
hello: 'world',
age: 10
},
d: 1
}
console.log(expectedOptions)
Update 2021-08-24
It seems this question is a duplicate of another so therefore I will delete it.
Here is a recursive solution, although it mutates the input object and doesn't return a new one, as I would've wanted. Hence why I added the deepCopy function.
Also, the order isn't maintained. But I assume that isn't of the biggest concern if you are using objects in the first place.
const defaultOptions = {
a: 0,
b: 1,
c: {
hello: 'world',
age: 20
}
}
const specialOptions = {
b: 0,
c: {
age: 10
},
d: 1
}
const deepCopy = obj => JSON.parse(JSON.stringify(obj))
const options = deepCopy(specialOptions)
const extendObj = (defaults, obj) => {
Object.entries(defaults)
.forEach(([key, value]) => {
if (obj[key] === undefined) {
obj[key] = value
} else if (typeof value === 'object') {
extendObj(defaults[key], obj[key])
}
})
}
extendObj(defaultOptions, options)
console.log(options)

How can I deeply map over object with Ramda

I'm trying to find all "template values" e.g. { template: 'Date: <now>'} using a map function to get this basic behaviour:
deepMap(mapFn, {a: 1, b: { c: 2, d: { template: 'Date: <now>'}}})
>> {a: 1, b: { c: 2, d: 'Date: 13423234232'}}
This is what I have so far. The interpolation of the template object does happen, but it does not replace the value.
const obj = {a: 1, b: { c: 2, d: { template: 'Date: <now>'}}};
const deepMap = (fn, xs) =>
mapObjIndexed(
(val, key, obj) =>
or(is(Array, val), is(Object, val))
? deepMap(fn, fn(val))
: fn(val),
xs
);
const checkFn = ({ template }) => template;
const transformFn = (val, key) => {
const interpolated = val.template.replace('<now>', Date.now())
console.log(interpolated);
return interpolated;
};
const mapFn = n =>
checkFn(n)
? transformFn(n)
: n;
console.clear();
deepMap(mapFn, obj);
>> {"a": 1, "b": {"c": 2, "d": {}}}
The problem is you are calling deepMap on the mapped value again - but the mapped value isn't an object anymore, but a string.
or(is(Array, val), is(Object, val))
? deepMap(fn, fn(val))
: fn(val),
In case val is { template: 'Date: <now>'}, val is an object and could be deep-mapped, but fn(val) is a String ("Date: 123123123") which should simply be returned. One solution is to make the is checks on the mapped value, not the original value:
(val, key) => {
const mappedVal = fn(val);
return or(is(Array, mappedVal), is(Object, mappedVal))
? deepMap(fn, mappedVal)
: mappedVal;
},
Another possibility would be to check whether the map-function returned something else than the original value and don't recurse in this case.
Something like this should work:
const {map, has, is} = R
const transformTemplate = ({template}) => template.replace('<now>', Date.now())
const deepMap = (xs) => map(x => has('template', x)
? transformTemplate(x)
: is(Object, x) || is(Array, x)
? deepMap(x)
: x, xs)
const result = deepMap({a: 1, b: { c: 2, d: { template: 'Date: <now>'}}})
// => {a: 1, b: {c: 2, d: "Date: 1542046789004"}}
console.log(result)
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.js"></script>
If you wanted to pass in the transformation function, you can change it slightly to
const deepMap = (transformer, xs) => map(x => has('template', x)
? transformer(x)
: is(Object, x) || is(Array, x)
? deepMap(transformer, x)
: x, xs)
const result = deepMap(transformTemplate, {a: 1, b: { c: 2, d: { template: 'Date: <now>'}}})
And of course you can wrap that in curry if you like.
I don't have time right now to investigate why this approach, which looks right at first glance, doesn't work. I'm hoping it's something simple:
const deepMap = map(cond([
[has('template'), transformTemplate],
[is(Object), deepMap],
[is(Array), deepMap],
[T, identity]
]))

Get same section of two objects in different type

I'd like to find best practice for getting the same section of two objects
const firstObject = { a: 1, b: 2, c: 3}
const secondObject = { 1, 2 }
// desired result: { a: 1, b: 2} or simply { a, b }
In my opinion, we need to do three steps:
1) Get value all values of each object
Object.values = Object.values || (obj => Object.keys(obj).map(key => obj[key]))
2) Find the same section from two arrays value
3) Find key-value pair from firstObject
Any other ways to do it?
Using Standard built-in objects as Array or Object is preferable
Break the firstObject into [key, value] pairs using Object#entries (or polyfill), and use Array#reduce to combine all those that exist in the secondObject.
const firstObject = { a: 1, b: 2, c: 3}
const secondObject = { 1: 1, 2: 2 };
const result = Object.entries(firstObject).reduce((obj, [key, value]) => {
value in secondObject && (obj[key] = value);
return obj;
}, {})
console.log(result);
Object.keys(firstObject).filter(key=> key in secondObject).reduce((o,k)=>(o[k]=firstObject[k],o),{});

How to remove undefined and null values from an object using lodash?

I have a Javascript object like:
var my_object = { a:undefined, b:2, c:4, d:undefined };
How to remove all the undefined properties? False attributes should stay.
You can simply chain _.omit() with _.isUndefined and _.isNull compositions, and get the result with lazy evaluation.
Demo
var result = _(my_object).omit(_.isUndefined).omit(_.isNull).value();
Update March 14, 2016:
As mentioned by dylants in the comment section, you should use the _.omitBy() function since it uses a predicate instead of a property. You should use this for lodash version 4.0.0 and above.
DEMO
var result = _(my_object).omitBy(_.isUndefined).omitBy(_.isNull).value();
Update June 1, 2016:
As commented by Max Truxa, lodash already provided an alternative _.isNil, which checks for both null and undefined:
var result = _.omitBy(my_object, _.isNil);
If you want to remove all falsey values then the most compact way is:
For Lodash 4.x and later:
_.pickBy({ a: null, b: 1, c: undefined }, _.identity);
>> Object {b: 1}
For legacy Lodash 3.x:
_.pick(obj, _.identity);
_.pick({ a: null, b: 1, c: undefined }, _.identity);
>> Object {b: 1}
The correct answer is:
_.omitBy({ a: null, b: 1, c: undefined, d: false }, _.isNil)
That results in:
{b: 1, d: false}
The alternative given here by other people:
_.pickBy({ a: null, b: 1, c: undefined, d: false }, _.identity);
Will remove also false values which is not desired here.
if you are using lodash, you can use _.compact(array) to remove all falsely values from an array.
_.compact([0, 1, false, 2, '', 3]);
// => [1, 2, 3]
https://lodash.com/docs/4.17.4#compact
Just:
_.omit(my_object, _.isUndefined)
The above doesn't take in account null values, as they are missing from the original example and mentioned only in the subject, but I leave it as it is elegant and might have its uses.
Here is the complete example, less concise, but more complete.
var obj = { a: undefined, b: 2, c: 4, d: undefined, e: null, f: false, g: '', h: 0 };
console.log(_.omit(obj, function(v) { return _.isUndefined(v) || _.isNull(v); }));
To complete the other answers, in lodash 4 to ignore only undefined and null (And not properties like false) you can use a predicate in _.pickBy:
_.pickBy(obj, v !== null && v !== undefined)
Example below :
const obj = { a: undefined, b: 123, c: true, d: false, e: null};
const filteredObject = _.pickBy(obj, v => v !== null && v !== undefined);
console.log = (obj) => document.write(JSON.stringify(filteredObject, null, 2));
console.log(filteredObject);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>
According to lodash docs:
_.compact(_.map(array, fn))
Also you can filter out all nulls
For deep nested object you can use my snippet for lodash > 4
const removeObjectsWithNull = (obj) => {
return _(obj)
.pickBy(_.isObject) // get only objects
.mapValues(removeObjectsWithNull) // call only for values as objects
.assign(_.omitBy(obj, _.isObject)) // save back result that is not object
.omitBy(_.isNil) // remove null and undefined from object
.value(); // get value
};
with pure JavaScript: (although Object.entries is ES7, Object.assign is ES6; but equivalent ES5 uses Object.keys only should be also doable); also notice v != null checks for both null and undefined;
> var d = { a:undefined, b:2, c:0, d:undefined, e: null, f: 0.3, s: "", t: false };
undefined
> Object.entries(d)
.filter(([ k, v ]) => (v != null))
.reduce((acc, [k, v]) => Object.assign(acc, {[k]: v}), {})
{ b: 2, c: 0, f: 0.3, s: '', t: false }
Edit: this below is the version with ES5 Object.keys only: but generally with ES7 in Node v8 is pretty much enjoyable ;-)
> Object.keys(d)
.filter(function(k) { return d[k] != null; })
.reduce(function(acc, k) { acc[k] = d[k]; return acc; }, {});
{ b: 2, c: 0, f: 0.3, s: '', t: false }
Update in October 2017: with Node v8 (since v8.3 or so) now it has object spreading construct:
> var d = { a:undefined, b:2, c:0, d:undefined,
e: null, f: -0.0, s: "", t: false, inf: +Infinity, nan: NaN };
undefined
> Object.entries(d)
.filter(([ k, v ]) => (v != null))
.reduce((acc, [k, v]) => ({...acc, [k]: v}), {})
{ b: 2, c: 0, f: -0, s: '', t: false, inf: Infinity, nan: NaN }
or within one reduce only:
> Object.entries(d)
.reduce((acc, [k, v]) => (v==null ? acc : {...acc, [k]: v}), {})
{ b: 2, c: 0, f: -0, s: '', t: false, inf: Infinity, nan: NaN }
Update: someone want recursive? isn't that hard either, just need an additional check of isObject, and recursively call itself:
> function isObject(o) {
return Object.prototype.toString.call(o) === "[object Object]"; }
undefined
> function dropNullUndefined(d) {
return Object.entries(d)
.reduce((acc, [k, v]) => (
v == null ? acc :
{...acc, [k]: (isObject(v) ? dropNullUndefined(v) : v) }
), {});
}
> dropNullUndefined({a: 3, b:null})
{ a: 3 }
> dropNullUndefined({a: 3, b:null, c: { d: 0, e: undefined }})
{ a: 3, c: { d: 0 } }
my conclusion: if pure Javascript can do, I would avoid any third party library dependencies:
To remove undefined, null, and empty string from object
_.omitBy(object, (v) => _.isUndefined(v) || _.isNull(v) || v === '');
I encountered a similar problem with removing undefined from an object (deeply), and found that if you are OK to convert your plain old object and use JSON, a quick and dirty helper function would look like this:
function stripUndefined(obj) {
return JSON.parse(JSON.stringify(obj));
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description
"...If undefined, a function, or a symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array)."
Since some of you might have arrived at the question looking to specifically removing only undefined, you can use:
a combination of Lodash methods
_.omitBy(object, _.isUndefined)
the rundef package, which removes only undefined properties
rundef(object)
If you need to recursively remove undefined properties, the rundef package also has a recursive option.
rundef(object, false, true);
See the documentation for more details.
Here's the lodash approach I'd take:
_(my_object)
.pairs()
.reject(function(item) {
return _.isUndefined(item[1]) ||
_.isNull(item[1]);
})
.zipObject()
.value()
The pairs() function turns the input object into an array of key/value arrays. You do this so that it's easier to use reject() to eliminate undefined and null values. After, you're left with pairs that weren't rejected, and these are input for zipObject(), which reconstructs your object for you.
Taking in account that undefined == null we can write as follows:
let collection = {
a: undefined,
b: 2,
c: 4,
d: null,
}
console.log(_.omit(collection, it => it == null))
// -> { b: 2, c: 4 }
JSBin example
I like using _.pickBy, because you have full control over what you are removing:
var person = {"name":"bill","age":21,"sex":undefined,"height":null};
var cleanPerson = _.pickBy(person, function(value, key) {
return !(value === undefined || value === null);
});
Source: https://www.codegrepper.com/?search_term=lodash+remove+undefined+values+from+object
You can also use Object.entries with Array.prototype.filter.
const omitNullish = (object) =>
Object.fromEntries(
Object.entries(object).filter(([, value]) => value != null)
)
omitNullish({ a: null, b: 1, c: undefined, d: false, e: 0 }) // { b: 1, d: false, e: 0}
If you want to use lodash, they are removing omit from v5 so an alternative is to use fp/pickBy along with isNil and negate.
import pickBy from 'lodash/fp/pickBy'
import isNil from 'lodash/isNil';
import negate from 'lodash/negate';
const omitNullish = pickBy(negate(isNil))
omitNullish({ a: null, b: 1, c: undefined, d: false, e: 0 }) // { b: 1, d: false, e: 0}
pickBy uses identity by default:
_.pickBy({ a: null, b: 1, c: undefined, d: false });
With lodash (or underscore) You may do
var my_object = { a:undefined, b:2, c:4, d:undefined, e:null };
var passedKeys = _.reject(Object.keys(my_object), function(key){ return _.isUndefined(my_object[key]) || _.isNull(my_object[key]) })
newObject = {};
_.each(passedKeys, function(key){
newObject[key] = my_object[key];
});
Otherwise, with vanilla JavaScript, you can do
var my_object = { a:undefined, b:2, c:4, d:undefined };
var new_object = {};
Object.keys(my_object).forEach(function(key){
if (typeof my_object[key] != 'undefined' && my_object[key]!=null){
new_object[key] = my_object[key];
}
});
Not to use a falsey test, because not only "undefined" or "null" will be rejected, also is other falsey value like "false", "0", empty string, {}. Thus, just to make it simple and understandable, I opted to use explicit comparison as coded above.
To omit all falsey values but keep the boolean primitives this solution helps.
_.omitBy(fields, v => (_.isBoolean(v)||_.isFinite(v)) ? false : _.isEmpty(v));
let fields = {
str: 'CAD',
numberStr: '123',
number : 123,
boolStrT: 'true',
boolStrF: 'false',
boolFalse : false,
boolTrue : true,
undef: undefined,
nul: null,
emptyStr: '',
array: [1,2,3],
emptyArr: []
};
let nobj = _.omitBy(fields, v => (_.isBoolean(v)||_.isFinite(v)) ? false : _.isEmpty(v));
console.log(nobj);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>
Shortest way (lodash v4):
_.pickBy(my_object)
If you don't want to remove false values. Here is an example:
obj = {
"a": null,
"c": undefined,
"d": "a",
"e": false,
"f": true
}
_.pickBy(obj, x => x === false || x)
> {
"d": "a",
"e": false,
"f": true
}
You can use lodash to remove null and undefined objects , but you should konw what lodash method you need to use, many dev uses isNil to remove the Null and undefined objects , but this function not remove the empty objects (' ')
you can use isEmpty to remove Null , Undefined and
import pickBy from 'lodash/fp/pickBy'
import negate from 'lodash/negate';
import isEmpty from 'lodash/isEmpty';
const omitNullish = pickBy(negate(isEmpty));
addressObject = {
"a": null,
"c": undefined,
"d": "",
"e": "test1",
"f": "test2
}
const notNullObjects = omitNullish(addressObject);
console.log(notNullObjects);
you will have this object : {
"e": "test1",
"f": "test2
}
var my_object = { a:undefined, b:2, c:4, d:undefined };
var newObject = _.reject(my_collection, function(val){ return _.isUndefined(val) })
//--> newCollection = { b: 2, c: 4 }
I would use underscore and take care of empty strings too:
var my_object = { a:undefined, b:2, c:4, d:undefined, k: null, p: false, s: '', z: 0 };
var result =_.omit(my_object, function(value) {
return _.isUndefined(value) || _.isNull(value) || value === '';
});
console.log(result); //Object {b: 2, c: 4, p: false, z: 0}
jsbin.
For deep nested object and arrays. and exclude empty values from string and NaN
function isBlank(value) {
return _.isEmpty(value) && !_.isNumber(value) || _.isNaN(value);
}
var removeObjectsWithNull = (obj) => {
return _(obj).pickBy(_.isObject)
.mapValues(removeObjectsWithNull)
.assign(_.omitBy(obj, _.isObject))
.assign(_.omitBy(obj, _.isArray))
.omitBy(_.isNil).omitBy(isBlank)
.value();
}
var obj = {
teste: undefined,
nullV: null,
x: 10,
name: 'Maria Sophia Moura',
a: null,
b: '',
c: {
a: [{
n: 'Gleidson',
i: 248
}, {
t: 'Marta'
}],
g: 'Teste',
eager: {
p: 'Palavra'
}
}
}
removeObjectsWithNull(obj)
result:
{
"c": {
"a": [
{
"n": "Gleidson",
"i": 248
},
{
"t": "Marta"
}
],
"g": "Teste",
"eager": {
"p": "Palavra"
}
},
"x": 10,
"name": "Maria Sophia Moura"
}
For those of you getting here looking to remove from an array of objects and using lodash you can do something like this:
const objects = [{ a: 'string', b: false, c: 'string', d: undefined }]
const result = objects.map(({ a, b, c, d }) => _.pickBy({ a,b,c,d }, _.identity))
// [{ a: 'string', c: 'string' }]
Note: You don't have to destruct if you don't want to.
I was able to do this in deep objects that include arrays with just one lodash function, transform.
Note that the double-unequal (!= null) is intentional as it will also match undefined, as is the typeof 'object' check as it will match both object and array.
This is for use with plain data objects only that don't contain classes.
const cloneDeepSanitized = (obj) =>
Array.isArray(obj)
? obj.filter((entry) => entry != null).map(cloneDeepSanitized)
: transform(
obj,
(result, val, key) => {
if (val != null) {
result[key] =
typeof val === 'object' ? cloneDeepSanitized(val) : val;
}
},
{},
);

Categories

Resources