iterating over Object of Objects to perform some logic on values - javascript

Here is my issue:
I have a JSON Object coming in a response from an http request, meaning not all objects inside the jsonResponse will be typeOf string, nor will I know how many objects are found inside the jsonResponse.
let jsonResponse = {
prop1: {
prop: 'objectKey1'
},
prop2: {
prop2: 'objectKey2',
num: 2,
stringy: 'stringy'
},
key: 'string',
anotherKey: 'anotherString',
prop3: {
objectKey3: 3,
objectKey4: 'string',
prop4: {
moreKeys: 'string',
num1: 666
}
},
property: 'anotherString2'
}
I want to go over each property/value inside this object, including all of it's child object keys and values in order to check if they are type of string.
if they are a type of string I want to send them to another function to perform a certain logic to that string, and to change it's value inside the jsonResponse.
I tried to do something like this -
Object.keys(jsonResponse).forEach((key, indexValue)=>{
if(typeof(jsonResponse[key])==='string'){
somelogic(jsonResponse[key])
} else if (typeof(jsonResponse[key] === 'object')){
//recurssive function to keep getting keys and values inside the jsonResponse
}
})
It seems that I'm missing some objects inside the jsonResponse because of that, and I don't know how to iterate through the child objects and their respected keys and values.
I know i'm not going through all objects, I'm just unsure how to do it recurssivly or if recurssive is the right way to do it.
I also tried using Object.entries(), but the tricky part for this is that I'll never know how many objects inside the jsonResponse are there, nor will I have any idea how many object of objects are there.
what is the fastest way to do it in manner of time-complexity?

let jsonResponse = {
prop1: {
prop: 'objectKey1'
},
prop2: {
prop2: 'objectKey2',
num: 2,
stringy: 'stringy'
},
key: 'string',
anotherKey: 'anotherString',
prop3: {
objectKey3: 3,
objectKey4: 'string',
prop4: {
moreKeys: 'string',
num1: 666
}
},
property: 'anotherString2'
}
function somelogic(a) {
console.log('>>>', a);
}
function getStringKeys(jsonResponse) {
Object.keys(jsonResponse).forEach((key, indexValue)=>{
if(typeof(jsonResponse[key])==='string'){
somelogic(jsonResponse[key])
} else if (typeof(jsonResponse[key] === 'object')){
getStringKeys(jsonResponse[key]);
}
})
}
getStringKeys(jsonResponse);

Related

Convert object containing objects to array containing objects

I have a JSON object that will sometimes be an object (a single instance) and sometimes be an array (multiple instances of the object). I need to write an if statement that basically says if this section of JSON is an object, wrap it in an array containing that single object, and if it's an array containing multiple objects, return that array. In either instance I'm returning an array containing either 1 or multiple objects.
Here is what the JSON looks like when it is NOT an array.
"link": {
"values": {
"key1": "value1",
...
"key8": "value8"
},
"key9": "value9"
}
And it should look like this when it's an array:
"link": [{
"values": {
"key1": "value1",
...
"key8": "value8",
},
"key9": "value9"
}]
EDIT -----------------------------
This is what I've written so far that is producing the type error I'm experiencing.
const isLinkArray = sections.values;
isLinkArray.link = Array.isArray(isLinkArray.link) ? isLinkArray.link : [isLinkArray.link];
EDIT 2 ---------------------------
The final answer ended up being almost identical to Kinglish' answer, so I figured I would post it here. The issue I ran into was that the JSON right above 'link' was also an array and that was causing the typescript error.
const sectionsWithLinkArray = sections.map((section) => {
  return {
    values: {
      ...section.values,
      link: !Array.isArray(section.values.link) ? [section.values.link] : section.values.link,
    },
  };
});
You can use Array.isArray to check, then convert
let data = {
"link": {
"values": {
"key1": "value1",
"key8": "value8"
},
"key9": "value9"
}
}
data.link = Array.isArray(data.link) ? data.link : [data.link];
console.log(data)
This can be done by writing a simple function that checks if it's an array.
const returnArray = (value) => {
if (Array.isArray(value) {
return value;
}
return
}
updated the answer of #Kinglish to typescript one because you cannot change types of defined value as it giving error for this either simply ignore the typescript or define types that simply accept link in object and array of object or just created new variable that expect a link in the array and wrap it inside data by simply doing this:
const data = {
link: {
values: {
key1: 'value1',
key8: 'value8',
},
key9: 'value9',
},
};
// This is the type of data you can't change it by default and it doesn't expect array of object of `link`.
// const data: {
// link: {
// values: {
// key1: string;
// key8: string;
// };
// key9: string;
// };
// };
const linkArray = { link: Array.isArray(data.link) ? data.link : [data.link] };
// Now this is the type of linkArray that expect array of object of `link`
// const linkArray: {
// link: {
// values: {
// key1: string;
// key8: string;
// };
// key9: string;
// }[];
// };
console.log('data', data);
console.log('linkArray', linkArray);

Normalise the JSON where Array as an value in javascript

I was trying to normalize a very deeply nested JSON which contains all possible ways JSON can be created. A part of JSON can be seen in below code snippet.
What is my end goal
I am converting the nested JSON into a simple JS object like below
{
key1: value,
key2: value,
...
}
Problem i faced with the below solution is that when it comes to Objects with values as array
i failed to find a way to see its key values.
if you run below code
key4,key5, key6 wont get displayed with the console.log only its value gets printed.
key1 -- values
key2 -- values
key3 -- value3
0 --
0 -- some_value
Code snippet
const req = {
request: {
results: {
key1: 'values',
results: [
{
key2: 'values',
},
],
},
params: {
key3: 'value3',
query: {
key4: [''],
key5: ['123456'],
key6: ['some_value'],
},
},
},
};
function normaliseJSON(obj) {
for (let k in obj) {
if (obj[k] instanceof Object) {
normaliseJSON(obj[k]);
} else {
console.log(`${k} -- ${obj[k]}`);
}
}
}
normaliseJSON(req);
Is there any way to get the keys of key4,5,6 ?
also open to any other solution to normalise such JSON
The reason your recursion goes inside the array is since ['123456'] instanceof Object is true in javascript (typeof(['asd']) also gives "object"). To check if something is an array have to check with Array.isArray(something)
In template literals when you try to embed an array eg ...${['123456']} in the end it will show as ...123456 without the brackets. Therefore in situation of Arrays need to JSON.stringify(arr)
There may be better ways of doing this but I created a function called arrayHasObject which checks if an array has object elements. This was to catch the inner results array and ignore key4,key5 and key6.
The recursion will happen if obj[k] is an object and not an array or if obj[k] is an array and it has an object element.
Since recursion is hard to visualize I recommend https://pythontutor.com/ . It is mostly for Python but works for JS as well. It can help you visualize these things and to find where things go wrong
Ofcourse the way I have written it will break if something like key4: [{a:'abc'}] since arrayHasObject gives true for this. Maybe will need to change the function accordingly.
function arrayHasObject(arr) {
return arr.some((x) => typeof(x)==='object' && !Array.isArray(x))
}
const req = {
request: {
results: {
key1: 'values',
results: [
{
key2: 'values',
},
],
},
params: {
key3: 'value3',
query: {
key4: [''],
key5: ['123456'],
key6: ['some_value'],
},
},
},
};
function normaliseJSON(obj) {
for (let k in obj) {
if ((obj[k] instanceof Object && !Array.isArray(obj[k])) || (Array.isArray(obj[k]) && arrayHasObject(obj[k]))) {
normaliseJSON(obj[k]);
} else {
if (Array.isArray(obj[k])){
console.log(`${k} -- ${JSON.stringify(obj[k])}`);
}
else{
console.log(`${k} -- ${obj[k]}`);
}
}
}
}
normaliseJSON(req);

Key exist in object in object

I need to check key exist in object of object. I have array of object and in every object i have one other object. I need to check the key is existing in object of object
var myarr = [{
hello: "world",
payload: {
kekek: 'sdfsdfsdf',
baby: 'sdfsdfsdfds'
}
},
{
hello: "world",
payload: {
qwe: 'sdfsdfsdf',
baby: 'sdfsdfsdfds'
}
}, {
hello: "world",
payload: {
qwe: 'sdfsdfsdf',
baby: 'sdfsdfsdfds'
}
},
{
hello: "world",
payload: {
asdf: 'sdfsdfsdf',
baby: 'sdfsdfsdfds'
}
}
]
let pp = myarr.payload.hasOwnProperty('eekey')
console.log(pp).
I need to check kekek in payload.
If I understood correctly, you want to check if every of object of your array contains a specific key in payload property. If so, you can use in operator to check if a property is present in a object. You can improve this snippet by checking if value is defined.
let key = 'kekek';
const everyObjectHasKey = myarr.every(item => item.payload && key in item.payload);
console.log(everyObjectHasKey);
In this link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every , you can see more about every method for arrays

Edit a specific object property keeping the same object structure

Let's say I have this object:
a = {
key1: {
name: 'a',
import: 1234.7896,
discount: 122.34553
}
key2: {
name: 'b'
import: 8976.09998,
discount: 12.890999
}
}
and I have a function which format the number in some way and returning a string. Applying that function I want an object like this:
result = {
key1: {
name: 'a'
import: '1,234.78'
discount: '122.33'
}
key2: {
name: 'b'
import: '8,976.09'
discount: '12.89'
}
}
So, my goal is to take all the properties which contains numbers and format them.
How can I iterate the object to edit just the properties which contains a number and return another object with the same key and different numeric properties?
The natural approach to this task is recursion. Below I've assumed you have a format function that formats a number into a string as you desire, and defined recurseFormat to recurse through object values, looking for values of type "number" to run format on. You might need to modify the typeof value === 'object' test to suite your needs for when to recurse.
function format(number) {
...
}
function recurseFormat(object) {
for (const [key, value] of Object.entries(object)) {
if (typeof value === 'number') {
object[key] = format(value)
} else if (typeof value === 'object') {
recurseFormat(value);
}
}
}
Note that this code modifies your object in place. If you want to deep copy your object at the same time, that can be done with a similar recursive approach; let me know and I can try writing that.
You can implement recursion which will work for nested object as well. Something like this:
const convertToString=obj=>{
return Object.fromEntries(Object.entries(obj).map(([k,v])=>[k, typeof v=="object" ? convertToString(v) : v.toLocaleString()]))
};
const a = { key1: { name: 'a', import: 1234.7896, discount: 122.34553 }, key2: { name: 'b', import: 8976.09998, discount: 12.890999 } };
console.log(convertToString(a));

How to return new array with dynamically populated properties?

So my call returns something like:
data:
{
nameData: 'Test33333',
emailData: email#email.com,
urlLink: link.com
additionalDetails: [
{
field: 'email',
value: 'other#email.com'
},
{
field: 'name',
value: 'name1223'
}
]
}
Now, I want to make a function that would take the passed parameter (data) and make an array of objects, that should look like below. It should be done in more generic way.
Array output expectation:
fullData = [
{
name: 'data_name'
value: 'Test33333'
},
{
name: 'data_email',
value: 'email#email.com'
},
{
name: 'data_url',
value: 'Link.com'
},
extraData: [
//we never know which one will it return
]
];
It should be done in the function, with name, for example:
generateDataFromObj(data)
so
generateDataArrFromObj = (data) => {
//logic here that will map correctly the data
}
How can this be achieved? I am not really proficient with JavaScript, thanks.
Assuming that you keep your data property keys in camelCase this will work for any data you add, not just the data in the example. Here I've used planetLink. It reduces over the object keys using an initial empty array), extracts the new key name from the existing property key, and concatenates each new object to the returned array.
const data = { nameData: 'Test33333', emailData: 'email#email.com', planetLink: 'Mars' };
function generateDataArrFromObj(data) {
const regex = /([a-z]+)[A-Z]/;
// `reduce` over the object keys
return Object.keys(data).reduce((acc, c) => {
// match against the lowercase part of the key value
// and create the new key name `data_x`
const key = `data_${c.match(regex)[1]}`;
return acc.concat({ name: key, value: data[c] });
}, []);
}
console.log(generateDataArrFromObj(data));
Just run a map over the object keys, this will return an array populated by each item, then in the func map runs over each item, build an object like so:
Object.keys(myObj).map(key => {return {name: key, value: myObj[key]}})

Categories