access nested object values dynamically using JavaScript [duplicate] - javascript

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);

Related

Merge nested array of objects by comparing key [duplicate]

This question already has answers here:
How can I merge properties of two JavaScript objects dynamically?
(69 answers)
Closed 9 months ago.
I have an 3 nested array of Objects and I need to merge them all in single array of objects by comparing key. and if any object have same key then their inner object will merge. And if key is not same then also the nested array of objects will merge*
var obj1 = {
a: {
c: 3
}
}
var obj2 = {
b: {
e: 40
}
}
var obj3 = {
d: {
x: 30
},
a: {
f: 66
}
}
// The expected output will be like this -
//
/*OUTPUT:::
{
a: {
c: 3,
f: 66
},
b: {
e: 40
},
d: {
x: 30
}
}
Please tell me which approach would be suitable ?
You can group the objects using Array.prototype.reduce.
const
obj1 = { a: { c: 3 } },
obj2 = { b: { e: 40 } },
obj3 = { d: { x: 30 }, a: { f: 66 } };
const res = [obj1, obj2, obj3].reduce((r, o) => {
Object.entries(o).forEach(([k, v]) => {
r[k] = { ...r[k], ...v };
});
return r;
}, {});
console.log(res);

Reordering an object to the keys are in the same order as an array

I have an array that looks like this,
['event_tag', 'workflow_tag', 'created_timestamp', 'success']
and an array of objects where the object looks like,
{
"created_timestamp": "2022-04-01T13:14:53.028002Z",
"workflow_tag": "dj807",
"event_tag": "refresh",
"success": true
}
What I am wanting to do is make the above object and any other objects in that array match the order of the values in the first array so the finished object should look like,
{
"event_tag": "refresh",
"workflow_tag": "dj807",
"created_timestamp": "2022-04-01T13:14:53.028002Z",
"success": true
}
I have tried the following so far,
const keys = ['event_tag', 'workflow_tag', 'created_timestamp', 'success'];
newRowData = parsedRows.reduce((obj, v) => {
obj[v] = keys[v];
return obj
}, {});
But this returns,
{[object Object]: undefined}
You could order the keys by constructing a new object inside of an Array#map:
const parsedRows = [ { a: 1, c: 3, d: 4, b: 2, }, { b: 6, a: 5, c: 7, d: 8, }, { d: 12, b: 10, a: 9, c: 11, }, ];
const order = ['a', 'b', 'c', 'd'];
let newData = parsedRows.map(row => {
let newRow = {};
for (let key of order) {
newRow[key] = row[key];
}
return newRow;
});
console.log(newData);
Instead of iterating over Rows, Iterate on keys either map/reduce.
const keys = ["event_tag", "workflow_tag", "created_timestamp", "success"];
const obj = {
created_timestamp: "2022-04-01T13:14:53.028002Z",
workflow_tag: "dj807",
event_tag: "refresh",
success: true,
};
const res = Object.assign({}, ...keys.map((key) => ({ [key]: obj[key] })));
console.log(res)

How to deep pick in another pick with lodash?

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',
};
*/

Get all unique values in an array (remove duplicates) fot nest array / object

i know there has many answer for unique array
but they can't handle with array of array
what i want is
source array
[
1,
0,
true,
undefined,
null,
false,
['a', 'b', 'c'],
['a', 'b', 'c'],
['a', 'c', 'b'],
{ a: { b: 2 } },
{ a: { b: 2 } },
{ a: { b: 3 } },
{ a: { b: undefined } },
{ a: { } },
{ a: { b: 3, c: undefined } },
]
the return
[
1,
0,
true,
undefined,
null,
false,
['a', 'b', 'c'],
['a', 'c', 'b'],
{ a: { b: 2 } },
{ a: { b: 3 } },
{ a: { b: undefined } },
{ a: { } },
{ a: { b: 3, c: undefined } },
]
arr-unique can handle object[], but can't handle array of array
Set can't too
fail code
console.log(array_unique(data));
console.log([...new Set(data)]);
console.log(data.filter(function (el, index, arr)
{
return index == arr.indexOf(el);
}));
===================
update
i create a module for this array-hyper-unique, but didn't use json stringify because it has a bug when valuse is regexp
One easy method would be to stringify the arrays and objects in order to identify duplicates:
const input = [
1,
true,
['a', 'b', 'c'],
['a', 'b', 'c'],
{ a: { b: 2 } },
{ a: { b: 2 } },
{ a: { b: 3 } },
{ a: { b: 3, c: undefined } },
];
const outputSet = new Set();
const stringifiedObjs = new Set();
input.forEach(item => {
if (typeof item !== 'object') outputSet.add(item);
else {
// replace undefineds with something, else they'll be ignored by JSON.stringify:
const stringified = JSON.stringify(
item,
(k, v) => v === undefined ? 'undefined-value' : v
);
if (!stringifiedObjs.has(stringified)) {
outputSet.add(item);
stringifiedObjs.add(stringified)
}
}
});
console.log([...outputSet]);
Try by converting elements to string using JSON.stringify and use indexOf to push these elements to another array,only if the another array does not contain this element. Then again use map & JSON.parse to convert string to the original format
var data = [
1,
true, ['a', 'b', 'c'],
['a', 'b', 'c'],
{
a: {
b: 2
}
},
{
a: {
b: 2
}
},
{
a: {
b: 3
}
},
]
// Create a new array of only string
// map will give new array and JSON.stringify will convert elements to string
var newData = data.map(function(item) {
return JSON.stringify(item)
})
//An empty array which will contain only unique values
var uniques = [];
// loop over the array of stirngs and check
//if that value is present in uniques array
//if not then push the element
newData.forEach(function(item) {
if (uniques.indexOf(item) === -1) {
uniques.push(item)
}
});
//Convert array of string to json
var parsedArr = uniques.map(function(item) {
return JSON.parse(item)
});
console.log(parsedArr)
The reason you method does not work, is because the first ['a', 'b', 'c'], and the second ['a', 'b', 'c'] are different objects, as are the first and second instances of { a: { b: 2 } }.
Because of this, even though you add them to Set, they will be considered non-equivalent to each other, and therefore, not be filtered for uniqueness.
It seems you want to get a unique array based on the absolute values in each object. One easy way to do this is to use the ES6 Map like so:
function uniq(arr) {
var uniqMap = new Map()
arr.forEach(element => {
uniqMap.set(JSON.stringify(element), element)
})
return [...uniqMap.values()]
}
You can then get the result you are looking for:
uniq(data)
//Result: [ 1, true, [ 'a', 'b', 'c' ], { a: { b: 2 } }, { a: { b: 3 } } ]
You could take a recursive approach for objects and check the values.
function check(a, b) {
if (!a || typeof a !== 'object') {
return a === b;
}
var keys = Object.keys(a);
return keys.length === Object.keys(b).length
&& keys.every(k => k in b && check(a[k], b[k]));
}
var array = [1, 0, true, undefined, null, false, ['a', 'b', 'c'], ['a', 'b', 'c'], ['a', 'c', 'b'], { a: { b: 2 } }, { a: { b: 2 } }, { a: { b: 3 } }, { a: { b: undefined } }, { a: {} }, { a: { b: 3, c: undefined } }],
unique = array.reduce((r, b) => (r.some(a => check(a, b)) || r.push(b), r), []);
console.log(unique);
.as-console-wrapper { max-height: 100% !important; top: 0; }

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);

Categories

Resources