How to deep pick in another pick with lodash? - javascript

Now i have code like this:
var object = {
a: 'a',
b: 'b',
c: {
d: 'd'
}
}
_.get(object).pick(['a', 'b']).value();
How to deep pick property 'd' like:
_.get(object).pick(['a', 'b', 'c.d']).value();

you can deep destructure without lodash :
var object = {
a: 'a',
b: 'b',
c: {
d: 'd'
}
}
const { a, b, c :{ d }} = object;
console.log(a,b,d);
const obj = {a, b, d};
console.log(obj);

In case you insist in using Lodash, consider using the _.get() function:
_.get(object, 'c.d');
So, for the properties you want to get:
const selectedProps = {
..._.pick(object, ['a', 'b']),
_.get(object, 'c.d')
}

You can create a flatPick() function. The function iterates the array of paths. and uses _.get() to get the value of the path, and _.set() to add the last part of the path as property on the result object:
function flatPick(object, paths) {
const o = {};
paths.forEach(path => _.set(
o,
_.last(path.split('.')),
_.get(object, path)
));
return o;
}
var object = {
a: 'a',
b: 'b',
c: {
d: 'd',
e: {
f: 'f'
}
}
};
var result = flatPick(object, ['a', 'b', 'c.d', 'c.e.f']);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

This can work for lodash:
function pickNested(object: Json, fields: string[]) {
const shallowFields = fields.filter((field) => !field.includes('.'));
const deepFields = fields.filter((field) => field.includes('.'));
const initialValue = _.pick(object, shallowFields) as Json;
return deepFields.reduce((output, field) => {
const key = _.snakeCase(field);
output[key] = _.get(object, field);
return output;
}, initialValue);
}
and:
const json = {
id: '10',
user: {
email: 'david.i#example.com',
},
};
const newData = pickNested(json, ['id', 'user.email']);
console.log('newData ->', newData);
/*
{
id: '10',
user_email: 'david.i#example.com',
};
*/

Related

Group object by value type

I have this object:
const data = {
a: 'name',
b: 'age',
c: 'age',
d: 'age',
e: 'name',
f: 'age'
}
and I want to group by values so I would like to have:
const result = {
name: ['a', 'e'],
age: ['b', 'c', 'd', 'f']
}
Is there a smart way to do that?
Tersest version, thanks Nick for the comma operator to save the curlies
const data = { a: 'name', b: 'age', c: 'age', d: 'age', e: 'name', f: 'age' },
arr = Object.entries(data)
.reduce((acc, [key,value]) => ((acc[value] ??=[]).push(key), acc),{});
console.log(arr)
Explanations since this is interesting to add to one's arsenal
(acc, // accumulator defined by the {} at the end of the statement
[key,value] // destructing the entries
) =>
( // bracket to set up the comma operator later
(acc[value] ??=[]) // nullish coalescing assignment - if no acc[value] then assign an array
.push(key)
, acc) // comma operator returns what is after the comma
,{});
You can iterate over all the keys and check if the corresponding value has been already assigned as key to your result object; if so, push the key into the array, if not initialise it as an array.
Something like this will work:
const data = { a: 'name', b: 'age', c: 'age', d: 'age', e: 'name', f: 'age' }
const output = {};
Object.keys(data).forEach(key => {
if (!output[data[key]]) {
// new key: assign it as a 1 element array
output[data[key]] = [key];
} else {
// existing key: push it into the array
output[data[key]].push(key);
}
});
console.log(output);
Some good solutions added, but could be more clear with Object.entries ?
const data = { a: 'name', b: 'age', c: 'age', d: 'age', e: 'name', f: 'age' }
const results = {};
Object.entries(data).forEach(([key, value]) => {
if (results[value]) {
results[value].push(key);
} else {
results[value] = [key];
}
});
console.log(results)
One way to do so:
const map = new Map;
Object.keys(data).forEach(key => {
const value = data[key];
if (!map.has(value))
map.set(value, []);
map.get(value).push(key);
});
const result = {};
map.forEach((key, value) => {
result[value] = key;
});
console.log(result);
Another way:
const data = { a: 'name', b: 'age', c: 'age', d: 'age', e: 'name', f: 'age' }
const newData = {};
Object.entries(data).forEach(pair => {
const [key, value] = pair;
if (!newData[value])
newData[value] = [];
newData[value].push(key)
});
console.log(newData)

Deleting key to nested object?

In this question I got some excellent answers, but it turned out I had over simplified the problem I am facing.
I have the same object
const obj = {
a: 'A',
b: {
bb: 'BB',
bbb: 'BBB',
},
c: 'C'
};
and I somehow need to end up with
{ a: 'A', bb: 'BB', bbb: 'BBB', c: 'C' }
where there key to the nested object is removed. It doesn't have to be in place. Creating a new object is fine.
Question
Can anyone figure out how to delete the key from the nested object, but still keep the nested object?
You can flatten it recursively:
function flattenObject(obj) {
const ret = {};
for (const [key, val] of Object.entries(obj)) {
if (typeof val === "object") {
Object.assign(ret, flattenObject(val));
} else {
ret[key] = val;
}
}
return ret;
}
Get an array of [key, value] pairs using Object.entries(). Iterate the pairs with Array.flatMap(). If the value is object, call the function with the value. If not return an object of { [key]: value }. Merge to a single object by spreading the array of object into Object.assign().
Use recursion and merge the sub objects by spreading into Object.assign():
const fn = obj => Object.assign({},
...Object.entries(obj)
.flatMap(([k, v]) => typeof(v) === "object" ? fn(v) : { [k]: v })
);
const j = { a: 'A', b: { bb: 'BB', bbb: 'BBB' }, c: 'C' };
const result = fn(j);
console.log(result);
Another option is to work directly with the [key, value] entries (p), and then convert everything to a single object using Object.fromEntries():
const fn = obj =>
Object.entries(obj)
.flatMap(p => typeof(p[1]) === "object" ? fn(p[1]) : [p])
const j = { a: 'A', b: { bb: 'BB', bbb: 'BBB' }, c: 'C' };
const result = Object.fromEntries(fn(j));
console.log(result);

access nested object values dynamically using JavaScript [duplicate]

This question already has answers here:
Accessing nested JavaScript objects and arrays by string path
(44 answers)
Closed 2 years ago.
Assume I have an object:
var abc = {
a: 'a',
b: 'b',
c: {
x: 'c',
y: 'd'
}
}
Now I want to fetch object values based on values present in an array below dynamically
var arr = ['a', 'b', 'c.x']
NOTE: the following solution would work with the given scenario, where object properties are indicated using the dot notation, but will fail if you use bracket notation (e.g. c[x]).
const abc = {
a: 'aVal',
b: 'bVal',
c: {
x: 'cxVal',
y: 'cyVal'
},
d: {
x: 'dxVal',
y: {
z: 'dyzVal',
w: 'dywVal'
}
}
};
const arr = ['a', 'b', 'c.x', 'd.y.w'];
function getValues(obj, keysArr) {
return keysArr.map(key => {
return key.split('.').reduce((acc, item) => {
return acc[item];
}, obj);
});
}
const values = getValues(abc, arr);
console.log(values);
You can create an extension like this:
Object.defineProperty(Object.prototype, 'valueAt', {
value: function (location, defaultValue) {
const routes = location.split('.');
const lastRoute = routes.pop();
let value = routes.reduce(
(current, route) => current && current[route],
this
);
if (value) return value[lastRoute] || defaultValue;
else return defaultValue;
},
writable: true,
configurable: true,
enumerable: false,
});
and then use:
abc.valueAt("c.x");
You could split the strings for getting an array and reduce the keys by accessing the object.
var getValue = (object, keys) => keys.reduce((o, k) => (o || {})[k], object),
object = { a: 'a', b: 'b', c: { x: 'c', y: 'd' } },
keys = ['a', 'b', 'c.x'],
result = keys.map(s => getValue(object, s.split('.')));
console.log(result);

Create new object based on filtered keys

Suppose I have an array of strings that represent keys such as ['a', 'b', 'd'], and an existing object such as...
const obj = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5
}
Is there a method of creating a new object that is a filtered version of obj based on the keys in the array such that...
const updated = {
a: 1,
b: 2,
d: 4
}
using the Object.assign() function?
I know it works with a function such as...
function createNew(o, keys) {
const updated = {}
Object.keys(o).forEach(k => {
if (keys.includes(k)) updated[k] = o[k]
})
return updated
}
but I'm looking for a solution with Object.assign()
const obj = {
a: 1,
b: 2,
c: 3,
d: 4,
e: 5
};
const desiredKeys = ['a', 'c', 'd'];
const result = desiredKeys.reduce((acc, key) => {
acc[key] = obj[key];
return acc;
}, {});
console.log(result);

New array of objects based on an array inside an object?

need to create a shallow array of objects based on the elements of an array that is an object's value:
var obj = {
a: 1,
b: 'x',
c: 'z',
d: ['rr', 'qq']
};
var rec = [];
obj.d.forEach(function(e, i) {
rec.push({
d: e
})
});
console.log(rec);
But of course this only gets me
[ { d: 'rr' }, { d: 'qq' } ]
How to get to this in a new array of objects? -->
[ { a: 1,
b: 'x',
c: 'z',
d: 'rr' },
{ a: 1,
b: 'x',
c: 'z',
d: 'qq' } ]
The easiest way to get the desired result would be to use the map function (which maps elements of one array to a new array using a given mapping function). You can then create new objects re-using a, b, and c from the original object and d from the mapping function parameters:
var rec = obj.d.map(function(r) {
return {
a: obj.a,
b: obj.b,
c: obj.c,
d: r
};
});
obj.d.forEach(function(e) {
var item = {};
Object.keys(obj).forEach(function(key) {
item[key] = obj[key];
});
item.d = e;
rec.push(item);
});
But properties a, b, c can't be objects. Otherwise each item in the rec array will have the same reference.
Alternately, this works, but it is quite ugly:
var o = {
a: 1,
b: 'x',
c: 'z',
d: ['rr', 'qq']
};
var arr = [];
Object.keys(o).forEach(function(k) {
var val = o[k];
if (Array.isArray(val)) {
val.forEach(function(j) {
arr.push({[k] : j});
});
}
});
arr.forEach(function(obj) {
for (var p in o) {
var val = o[p];
if (!Array.isArray(val)) {
obj[p] = val
}
}
});
console.log(arr);

Categories

Resources