I have sample JSON like this
`{
values:[{
"name": "Base Url",
"url": "https://kubemanagement-prod.kohls.com"
},
{
"name": "Base Url newwww",
"url": "https://batman.com"
}]
}`
Currently when I add this paticular JSON to the lodash _.keys gives me the result ["0", "1"] which is basically the index of first and second object 0 and 1.
What I exactly want is to retrieve all the keys of the JSON object including sub object properties as well. In this case ["values","0", "1","name","url"]
Does anyone knows a lodash method or a mechanism to retrieve all the keys given in complex JSON object to nth level?
language : Angular + Typescript
This function recursively gets the keys from an objects tree, using _.keys() and _.flatMap() and _.union() to combine lists of keys, and get only the unique values:
const getAllKeys = obj => _.union(
_.keys(obj),
_.flatMap(obj, o => _.isObject(o) ? getAllKeys(o) : [])
)
const arr = {"values": [{"name":"Base Url","url":"https://kubemanagement-prod.kohls.com"},{"name":"Base Url newwww","url":"https://batman.com"}]}
const result = getAllKeys(arr)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
And the same idea without lodash, using Object.values() and Array.flatMap() to iterate the current object (or array), and Array.concat() and a Set to make the keys unique:
const getAllKeys = obj => [...new Set([].concat(
Object.keys(obj),
Object.values(obj).flatMap(o => typeof o === 'object' ? getAllKeys(o) : [])
))]
const arr = {"values": [{"name":"Base Url","url":"https://kubemanagement-prod.kohls.com"},{"name":"Base Url newwww","url":"https://batman.com"}]}
const result = getAllKeys(arr)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
Without array indexes:
const getAllKeys = obj => _.union(
_.isArray(obj) ? [] : _.keys(obj),
_.flatMap(obj, o => _.isObject(o) ? getAllKeys(o) : [])
)
const arr = {"values": [{"name":"Base Url","url":"https://kubemanagement-prod.kohls.com"},{"name":"Base Url newwww","url":"https://batman.com"}]}
const result = getAllKeys(arr)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
You don't need lodash for this, Object.keys is sufficient. You just write a recursive function, writing to a Set, perhaps converting to array when you're done:
const array = [
{
"name": "Base Url",
"url": "https://kubemanagement-prod.kohls.com"
},
{
"name": "Base Url newwww",
"url": "https://batman.com"
}
];
function addAll(set, entries) {
for (const entry of entries) {
set.add(entry);
}
return set;
}
function allKeys(obj/*: object|array*/, keys/*: Set<string>*/ = new Set()) {
addAll(keys, Object.keys(obj));
for (const entry of Object.values(obj)) {
if (entry && typeof entry === "object") {
allKeys(entry, keys);
}
}
return keys;
}
console.log([...allKeys(array)]);
Or using the structure in your edit:
const array = {
values:[{
"name": "Base Url",
"url": "https://kubemanagement-prod.kohls.com"
},
{
"name": "Base Url newwww",
"url": "https://batman.com"
}]
}
function addAll(set, entries) {
for (const entry of entries) {
set.add(entry);
}
return set;
}
function allKeys(obj/*: object|array*/, keys/*: Set<string>*/ = new Set()) {
addAll(keys, Object.keys(obj));
for (const entry of Object.values(obj)) {
if (entry && typeof entry === "object") {
allKeys(entry, keys);
}
}
return keys;
}
console.log([...allKeys(array)]);
How about
const arr = {"values": [{"name":"Base Url","url":"https://kubemanagement-prod.kohls.com"},{"name":"Base Url newwww","url":"https://batman.com"}]}
let result1 = Object.keys(arr) // get the object keys which is "values"
let result2 = Object.keys(arr[result1]); // get the keys of the object name "0,1" here
let result3 = Object.keys(arr[result1][0]); // get property names of one object "name and url" here
let resutltant = result1.concat(result2, result3) // "merge array"
console.log(resutltant)
Assuming that your object properties names will remain constant
If you use typescript why don't use Object.entries or Object.values to do that?
You need to add to tsconfig the next line:
lib: [
......
"es2018",
]
Related
I have this Object:
{
"data": {
"success": true,
"historical": true,
"date": "2022-01-01",
"base": "MXN",
"rates": {
"COFFEE": 0.02158734144632395,
"CORN": 0.008232645172711363,
"COTTON": 0.04320921676820366,
"SOYBEAN": 0.0036714622235960175,
"SUGAR": 0.25680398615582695,
"WHEAT": 0.00017592643558262669
},
"unit": "per bushel"
}
}
And I want to iterate over "rates" to replace the values of each key with 1 / value
I tried with: (prueba is the Object name)
Object.values(this.prueba.data.rates).forEach((val) => {
console.log(val)
val = 1 / val;
console.log(val)
})
But how to replace those values or how can I saved them in another array or Object
Your code doesn't work because the change you made to val is only reflected within the scope of the callback.
You should instead loop through each property and set its value.
const obj={data:{success:!0,historical:!0,date:"2022-01-01",base:"MXN",rates:{COFFEE:.02158734144632395,CORN:.008232645172711363,COTTON:.04320921676820366,SOYBEAN:.0036714622235960175,SUGAR:.25680398615582695,WHEAT:.00017592643558262669},unit:"per bushel"}};
let r = obj.data.rates;
Object.keys(r).forEach(e => r[e] = 1 / r[e])
console.log(obj)
If you're using Typescript it's necessary to declare the object like so:
const obj: {[key: string]: any} = ...
You can iterate over Object.entries (or Object.keys) and replace the value for each key.
let obj={data:{success:!0,historical:!0,date:"2022-01-01",base:"MXN",rates:{COFFEE:.02158734144632395,CORN:.008232645172711363,COTTON:.04320921676820366,SOYBEAN:.0036714622235960175,SUGAR:.25680398615582695,WHEAT:.00017592643558262669},unit:"per bushel"}};
Object.entries(obj.data.rates).forEach(([k, v]) => obj.data.rates[k] = 1 / v);
console.log(obj);
I think it will be cleaner to write it explicitly using for loops:
let obj = {} //original object stated in question
let obj2 = Object.create(null);//DON'T just use {}, see below
for(let productName in obj.data.rates){
let inverse = 1/obj.data.rates[productName];
//to edit the object directly
obj.data.rates[productName] = inverse;
//to add to another object
obj2[productName] = inverse;
}
The difference between {} and Object.create(null) can be found here
You can combine Object.entries(), Array#forEach() and Destructuring assignment
Code:
const obj = {data:{success:0,historical:0,date:"2022-01-01",base:"MXN",rates:{COFFEE:.02158734144632395,CORN:.008232645172711363,COTTON:.04320921676820366,SOYBEAN:.0036714622235960175,SUGAR:.25680398615582695,WHEAT:.00017592643558262669},unit:"per bushel"}}
const replaceValue = ({ data: { rates: r }}) =>
Object
.entries(r)
.forEach(([k, v]) => r[k] = 1 / v)
replaceValue(obj)
console.log(obj)
I have an object like this
{
metadata: {
correlationId: 'b24e9f21-6977-4553-abc7-416f8ed2da2d',
createdDateTime: '2021-06-15T16:46:24.247Z'
}
}
and I have an array of the properties I wanna access
[metadata, correlationId]
how can I dynamically access the property on the object?
like
keys.forEach((key) => {
object[key][key2] ???
})
it needs to be dynamic since I don't know how deep we need to access the object
Here is a solution without recursion:
const myObj = {
a: {
b: {
c: "I'm the target"
}
}
}
const keys = ['a', 'b', 'c'];
let result = myObj;
for (const key of keys) {
result = result[key];
}
console.log(result);
Or with recursion:
const finder = (obj, keys, index = 0) => {
const result = obj[keys[index++]];
if (!result) {
return obj;
}
return finder(result, keys, index);
}
console.log(finder(myObj, keys));
This is pretty similar to Accessing nested JavaScript objects and arrays by string path, except with one fewer step - you already have the keys you need in the form of an array. .reduce and access the next nested value in each iteration.
const obj = {
metadata: {
correlationId: 'b24e9f21-6977-4553-abc7-416f8ed2da2d',
createdDateTime: '2021-06-15T16:46:24.247Z'
}
};
const keys = ['metadata', 'correlationId'];
const result = keys.reduce((a, key) => a[key], obj);
console.log(result);
This is my idea to solve your problem. Tell me, if is ok for you.
let x = {
metadata: {
correlationId: 'b24e9f21-6977-4553-abc7-416f8ed2da2d',
createdDateTime: '2021-06-15T16:46:24.247Z'
}
}
let fun = x => typeof x === 'string' ? console.log(x) : Object.keys(x).map( y => fun(x[y]));
fun(x);
I have an Array of Objects. Every object in this Array has some Keypairs. One of this Keypairs ("obj", for example) is an Array of Objects too.
Example what I have:
const arrOfObj = [
{
"id": 1
"obj": {
"arr1": ["arr1-1"],
"arr2": ["arr2-1", "arr2-2"],
"arr3": ["arr3-1", "arr3-2"]
}
},
{
"id": 1
"obj": {
"arr1": ["arr1-2"],
"arr2": ["arr2-1", "arr2-3"],
"arr3": ["arr3-1", "arr3-3"],
"arr4": ["arr4-1"],
}
},
];
I need to get new Object of "obj" Objects with unique keys and unique elements inside them.
Example what I need:
const newObj = {
"arr1": ["arr1-1", "arr1-2"],
"arr2": ["arr2-1", "arr2-2", "arr2-3"],
"arr3": ["arr3-1", "arr3-2", "arr3-3"],
"arr4": ["arr4-1"],
}
All of this comes dynamically from API by request, so I don`t know the names of this keypairs, but i need to store them.
I have Solution, but I`m new in JavaScript, and want to know how to simplify and improve my poor Code.
1. First, I`m defining the new Object and retrieving the Names for his keypairs from "arrOfObj".
let filterObj = {};
arrOfObj.forEach(function (item) {
for (let key in item.obj) {
filterObj[key] = [];
}
});
2. After that I`m getting all the Elements of every Array from "arrOfObj" and store them in new Object "filterObj" in the Keypair with the same Name.
arrOfObj.forEach(function (item) {
for (let key in item.obj) {
for (let element = 0; element < item.obj[key].length; element++) {
filterObj[key].push(item.obj[key][element]);
}
}
});
3. To the end I`m filtering Arrays to get unique Elements only.
for (let key in filterObj) {
filterObj[key] = Array.from(new Set(filterObj[key]));
}
It works, I`ve got what I want, but it seems to much monstrously. How this code can be simplified the best way?
Thanks for the help and advices.
You can use some destructuring and Object.entries() and Object.keys() to streamline this and do everything to the new Object only
const newObj = {}
arrOfObj.forEach(({obj}) => {
Object.entries(obj).forEach(([k, arr]) => {
newObj[k] = newObj[k] || [];
newObj[k].push(...arr);
})
});
Object.keys(newObj).forEach(k => newObj[k] = [...new Set(newObj[k])]);
console.log(newObj)
<script>
const arrOfObj=[{id:1,obj:{arr1:["arr1-1"],arr2:["arr2-1","arr2-2"],arr3:["arr3-1","arr3-2"]}},{id:1,obj:{arr1:["arr1-2"],arr2:["arr2-1","arr2-3"],arr3:["arr3-1","arr3-3"],arr4:["arr4-1"]}}];
</script>
Another solution using Object#fromEntries, Array#reduce, Object#entries, Array#forEach, Set, and Map:
const arrOfObj = [ { "id": 1, "obj": { "arr1": ["arr1-1"], "arr2": ["arr2-1", "arr2-2"], "arr3": ["arr3-1", "arr3-2"] } }, { "id": 1, "obj": { "arr1": ["arr1-2"], "arr2": ["arr2-1", "arr2-3"], "arr3": ["arr3-1", "arr3-3"], "arr4": ["arr4-1"] } } ];
const filterObj =
// transform the resulting list of key-values pairs to an object at the end
Object.fromEntries(
// get a map of array name as key and its unique items as value
[...arrOfObj.reduce((map, { obj = {} }) => {
// iterate over current element's object to update the map
Object.entries(obj).forEach(([currentKey, currentValues]) => {
const keyValues = [...(map.get(currentKey) || []), ...currentValues];
map.set(currentKey, [...new Set(keyValues)]);
});
return map;
}, new Map)]
);
console.log(filterObj);
I have stuck at a point where I want to recursively traverse all the objects in an array and get the keys of those objects in a array data structure. I know how to loop over and get the keys of the object. But the problem here is I need that recursively for flexible objects. By flexible I mean it can have any level of nested properties.
So, what I have is an array like this:
let record = [{
"province": "string",
"city": "string",
"type": "alternative_address",
"address_line1": "string",
"post_code": "5858"
},
{
"province": "string",
"city": "string",
"type": "alternative_address",
"post_code": "5858",
"embedeer": {
"veryEmbedded": {
"veryveryEmbeded": 'yes'
}
}
}
];
And with some computation I am expecting an output like:
['province','city','type','address_line1','post_code','embedeer', 'embedeer.veryEmbedded', 'embedeer.veryEmbedded.veryveryEmbeded'];
For the effort I tried on this, I used the reduce() operation on array but I am unable to get that.
You need to write a recursive function that takes 2 inputs
object
prefix (undefined for first level keys)
let record = [{"province":"string","city":"string","type":"alternative_address","address_line1":"string","post_code":"5858"},{"province":"string","city":"string","type":"alternative_address","post_code":"5858","embedeer":{"veryEmbedded":{"veryveryEmbeded":"yes"}}}];
function addKeysToSet(o, p) {
Object.keys(o).forEach(k => {
let key = p ? p + "." + k : k; // Create the key hierarchy
keys.add(key); // Add key to the set
// If the value is an object, call the function recursively
if(typeof o[k] === 'object') {
addKeysToSet(o[k], key);
}
});
}
let keys = new Set(); // Create set of unique keys
// For each object in array, call function that adds keys to the set
record.forEach(o => addKeysToSet(o));
let result = Array.from(keys); // Create array from set
console.log(result); // logs result
You could take an iterative and recursive approach and take the power of a Set for getting unique values.
function iter(object, keys) {
return Object
.entries(object)
.reduce((r, [k, v]) => r.concat(keys.concat(k).join('.'), v && typeof v === 'object'
? iter(v, keys.concat(k))
: []
), []);
}
var record = [{ province: "string", city: "string", type: "alternative_address", address_line1: "string", post_code: "5858" }, { province: "string", city: "string", type: "alternative_address", post_code: "5858", embedeer: { veryEmbedded: { veryveryEmbeded: 'yes' } } }],
keys = [...record.reduce((s, o) => iter(o, []).reduce((t, v) => t.add(v), s), new Set)];
console.log(keys);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can Flatten the object and then get the keys.
// form https://gist.github.com/penguinboy/762197
let record = [{"province":"string","city":"string","type":"alternative_address","address_line1":"string","post_code":"5858"},{"province":"string","city":"string","type":"alternative_address","post_code":"5858","embedeer":{"veryEmbedded":{"veryveryEmbeded":"yes"}}}];
var flattenObject = function(a) {
var b = {};
for (var c in a)
if (a.hasOwnProperty(c))
if ("object" == typeof a[c]) {
var d = flattenObject(a[c]);
for (var e in d) d.hasOwnProperty(e) && (b[c + "." + e] = d[e]);
} else b[c] = a[c];
return b;
};
console.log(flattenObject(record) )
/*
It is also taking care of index numbers of the array. ("0.province" instead of "province" If multiple entries are passed)
*/
console.info( "All keys", Object.keys(flattenObject(record) ) )
// Simple
console.info( "keys", Object.keys(flattenObject(record[1]) ) )
var record1 = [{"province": "string","city": "string","type": "alternative_address","address_line1": "string","post_code": "5858" },
{ "province": "string","city": "string",
"type": "alternative_address",
"post_code": "5858",
"embedeer": {
"veryEmbedded": {
"veryveryEmbeded": 'yes'
}
}
}
];
var output = [];
function getAllKeys(obj,precedor="") {
var temp = Object.entries(obj);
temp.forEach((el) =>
typeof el[1] == "object" ? ( output.push(el[0]),getAllKeys(el[1],precedor==""? el[0]: precedor+"."+el[0])): output.push(precedor==""? el[0]: precedor+"."+el[0]));
}
record1.forEach((el,i) => getAllKeys(el,""));
//To avoid duplicate entries convert array to object.
console.log(...(new Set(output)));
I am trying to make something that takes arrays of strings, and then builds chains of nested objects that basically store what strings come after what in the input arrays. Initially, these chains had depths of 2, but I need to be able to generate higher-depth chains.
Basically, I need to take an array like this:
["test1", "test2", "test3", "test4"]
and convert it into this:
{
"test1":
{
"test2":
{
"test3":
{
"test4": {}
}
}
}
}
This looks like a job for Array#reduce:
function objectFromPath (path) {
var result = {}
path.reduce(function (o, k) {
return (o[k] = {})
}, result)
return result
}
var path = ["test1", "test2", "test3", "test4"]
console.log(objectFromPath(path))
.as-console-wrapper { min-height: 100%; }
I wanted to solve a similar problem, but I wanted to set a value at the end of the path within the resulting object, and I wanted to provide an initial object. I started with gyre's function and added some extra magic to satisfy my use case.
// Creates a nested object from an array representing the path of keys
//
// Takes 2 optional arguments:
// - a value to set at the end of the path
// - an initial object
//
// Ex: let house = objectFromPath(['kitchen', 'fridge'], 'empty', {civic: 123})
// => house = {civic: 123, kitchen: {fridge: 'empty'}}
const objectFromPath = (path, value = {}, obj = {}) =>
{
path.reduce((result, key, i, source) =>
{
if(i === (source.length - 1))
{
return (result[key] = value)
}
else
{
return (result[key] = {})
}
},
obj
)
return obj;
}
// Demo: house = {civic: 123, kitchen: {fridge: 'empty'}}
console.log(
objectFromPath(
['kitchen', 'fridge'],
'empty',
{civic: 123}
)
)