Convert object containing objects to array containing objects - javascript

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

Related

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

Setting the property of one specific key in an object in a reduce is setting it for all properties

Okay, I have literally no idea whats going on here. I'm assuming its some kind of reference issue? But I dont know how to get around it or whats causing it.
To sum it up, I have a list of objects, as well as an object that gets prepopulated to make sure I have data for all keys in the object.
I need to iterate over this list of objects, and by using the timeframeId in the metadata object, and the id in the data object, I want to assign the entire data object to the corresponding timeframeId and id hierarchy in the prepopulated object.
For some reason, all data properties are being overwritten to whatever the last row data is.
I've linked a repl so you can see for yourself: https://repl.it/#ThomasVermeers1/UnwrittenNoisyFirm#index.js
But my code is as follows:
const buildSegmentsFromRows = (rows, timeframeMetadata, defaultSegmentData) => {
// Prepopulate object to make sure every timeframe has a 'hello' key in it with some data
const emptySegments = timeframeMetadata.reduce((segmentMap, metadata) => {
segmentMap[metadata.timeframeId] = {
metadata,
segments: defaultSegmentData,
};
return segmentMap;
}, {});
// Now simply just loop over the rows, and set [row.metadata.timeframeId].segments[row.data.id] to row.data
const segments = rows.reduce((partialSegments, row) => {
const { timeframeId } = row.metadata;
const { id } = row.data;
/**
* This is the line where everything goes wrong
*/
partialSegments[timeframeId].segments[id] = row.data;
return partialSegments;
}, emptySegments);
return segments;
};
const rows = [
{
metadata: { timeframeId: '20202_01' },
data: {
'id': 'hello', 'value': 15
}
},
{
metadata: { timeframeId: '20202_02' },
data: {
'id': 'hello', 'value': 10
}
}
]
const timeframemetadata = [
{ timeframeId: '20202_01'},
{ timeframeId: '20202_02'}
]
const defaultSegmentData = {
'hello': {
'id': 'hello',
}
}
console.log(JSON.stringify(buildSegmentsFromRows(rows, timeframemetadata, defaultSegmentData), null, 2))
I'm expecting the end result to be:
{
"20202_01": {
"metadata": {
"timeframeId": "20202_01"
},
"segments": {
"hello": {
"id": "hello",
"value": 15
}
}
},
"20202_02": {
"metadata": {
"timeframeId": "20202_02"
},
"segments": {
"hello": {
"id": "hello",
"value": 10
}
}
}
}
But instead, value is getting set to 10 in all instances. I'm thinking its because we're setting the property to row.data, which is a reference, and gets updated on every call? But I'm at a complete loss here.
The problem is that you are referring to the same object for every segments in the list.
Therefore, changing the value of segments[id] will update defaultSegmentData, causing every reference to defaultSegmentData to change as well.
const emptySegments = timeframeMetadata.reduce((segmentMap, metadata) => {
segmentMap[metadata.timeframeId] = {
metadata,
segments: defaultSegmentData, // Everything goes wrong here.
};
return segmentMap;
}, {});
A simple solution to this problem is to avoid using the same reference to the object when creating the segmentMap:
const emptySegments = timeframeMetadata.reduce((segmentMap, metadata) => {
segmentMap[metadata.timeframeId] = {
metadata,
/** Or whatever default value you want.
* Just make sure to create a new instance of it for each call.
*/
segments: {},
};
return segmentMap;
}, {});

Typescript. Dynamically add property in nested object

Is possible dynamically add properties to nested object in Typescript ? Because my object is dynamic. In one case i have obj.
[
{someObject},
{
prop1:1,
columns: [
components: [
columns: [
components:[
{
type: number,
key: 'key1'
},
{
type: textfield,
key: 'key2'
}
]
]
]
]
}
]
And for example for object with key key2 i need add some flag. And in another case i get object less nested. Does exists any for example lodash function for this operation, or i have to iterate this object by recursion ?
You will have to recursively search. Thankfully this is not too difficult to implement:
const findAndUpdate = ($obj, $key, $val) => {
Object.keys($obj).includes($key) ?
$obj[$key] = $val
: Object.values($obj).forEach($nestedObj => findAndUpdate($nestedObj, $key, $val));
return $obj;
}
I used Object.<method> instead of lodash methods so that this can be easily replicated accross enviroments. This function will take in an initial object, the key you want to update and the value that you want to update it to.
Usage:
const foo = {
b: {
c: {
d: 5
}
}
}
findAndUpdate(foo, "d", 56);
console.log(foo) // -> { b: { c: { d: 56 } } } }
It checks if the key exists in the current layer of the object. If it doesn't then we call the function again for each object in the current layer - passing in the original object as a reference. If it does find a key in the current layer, then it will update the object that the reference points to. Eventually after the stack is cleared then we return our original updated object. Obviously if no keys are found that match the target key originally passed in then the object will remain unchanged.
If you want more customisation you could change the $val to take in a function $func instead:
const findAndUpdate = ($obj, $key, $func) => {
Object.keys($obj).includes($key) ?
$obj[$key] = $func($obj[$key])
: Object.values($obj).forEach($nestedObj => findAndUpdate($nestedObj, $key, $val));
return $obj;
}
Then you can now do something like this:
findAndUpdate(foo, "d", old => old+1 );
console.log(foo) // -> { b: { c: { d: 6 } } } }

How do I find a JSON value with a specific ID?

I have an object and I’m trying to find a specific value using an ID in ECMAScript6.
I’ve tried something like this: myvalue = this.json.find(x => x == '1234');
The JSON looks something like this:
{
"results": [
{
"abcde1234": {
"value": 4
}
},
{
"zxcv4567": {
"value": 2
}
}
]
}
All the examples I’ve found can only find off named key-value pairs.
Try
json.results.find(x => /1234/.test(Object.keys(x)[0]));
json = {
"results": [
{
"abcde1234": {
"value": 4
}
},
{
"zxcv4567": {
"value": 2
}
}
]
}
let r = json.results.find(x => /1234/.test(Object.keys(x)[0]));
console.log(r);
const json = {
'1234': { 'value' : 4},
'5678': { 'value' : 10}
};
const value = json['1234'];
console.log(value);
The JSON data doesn't seem proper.
But in case you are finding by key, you can directly access this, something like:
Parsed JSON maps directly to JavaScript types: Object, Array, boolean, string, number, null. Your example used find() which is (normally) a method used with arrays. If your JSON was structured like this, you could expect to use find:
const jsonString = '["a", "b", "c"]';
const jsonData = JSON.parse(jsonString);
jsonData.find(x => x === "a"); // "a"
But it seems like your data is structured as an object, so you can use normal property access:
const jsonString = '{"1234": {"value": 4}, "5678": {"value": 10}}';
const jsonData = JSON.parse(jsonString);
jsonData["1234"] // {value: 4}
jsonData["1234"].value // 4
EDIT
OP changed the data example, so the above code is less directly applicable, but the general point is: once you parse it, it's just javascript.

Categories