Unable to get length of JSON data in JS - javascript

I have a JSON file with some items. I'd like to randomly select one to display using Javascript. Here's the JSON file:
{
"keywords": [
{
"name": "item1",
"property1": "value1",
"property2": "value2",
"property3": "value3"
},
{
"name": "item2",
"property1": "value4",
"property2": "value5",
"property3": "value6"
}
]
}
I fetch the file as follows:
let data;
function fetchJSON() {
fetch('../keyworddata.json')
.then(response => {
if(!response.ok) throw new Error("HTTP status " + response.status);
return response.json();
})
.then(jsonData => {
data = jsonData;
})
.catch(error => {
console.error('Error:', error);
});
}
fetchJSON();
myData = JSON.parse(data);
lengthOfMyData = Object.keys(myData.keywords[0]).length;
console.log(lengthOfMyData);
However, I get the following error:
JSON Parse error: Unexpected identifier "object"
Can you help me? I'd like to find the number of items in the JSON file and then pick a random item from the list.

There's some weirdness going on here: you're using a global variable for data associated with a function, your second then is wrong in terms of what you named things (response.json() parses JSON, it does not return JSON. It returns a normal JS datastructure, leading to the next bit) and you're trying to parse regular JS data as if it's JSON.
So let's fix all of that:
function fetchJSON(filename) {
return fetch(filename)
.then(response => response.json())
.catch(error => {
// Log error, and return an object that the consuming
// code can still work with, but is effectively "empty"
console.error({error});
return { keywords: [] };
});
}
const { keywords } = await fetchJSON(`../keywords.json`);
console.log(keywords.length);
Although it would make far more sense to not make fetchJSON handle the errors, and instead have the calling code have a try/catch so that if the fetch fails for whatever reason, your calling code knows where to branch next.

Related

how to change this string to an array of objects [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 20 days ago.
Improve this question
SOLVED!:
The api had weird characters copied that were being sent over and breaking my javascript. always double check the values you are being sent!
To give full context on this:
to give the full context, i have a post api that is giving me the above structure in postman. i need to check the number and the code and if there are multiple objects returned. when i try to simply return the json i get the following error :json.parse()SyntaxError: Expected property name or '}' in JSON at position 2. Ive been trying to create work arounds to get the value but the best i can do is get it returned as a string using the code above. My goal is to check the code and the number of each object so i can validate against it. thank you for your help!
when returning my data from a post API I'm getting the response as a string. It's a formatted like an array of objects but I'm having difficulty parsing it due to the structure. the structure is as follows:
{
"items": [
{
"number": 119777777,
"code": "S",
"name": "name Full award ",
"year": 2021,
"updatedate": "04-JAN-2023"
},
{
"number": 119777777,
"code": "H",
"name": "Hospital funding.",
"year": 2021,
"updatedate": "04-JAN-2023"
}
]
}
how can I transform this into something usable? I need to check the values of the code in each object for validation.
heres my code:
function GetFunding() {
var responseClone; // 1
fetch('my url',{
method: "POST"
})
.then(function (response) {
responseClone = response.clone(); // 2
return response.json();
})
.then(function (data) {
// Do something with data
}, function (rejectionReason) { // 3
console.log('Error parsing JSON from response:', rejectionReason, responseClone); // 4
responseClone.text() // 5
.then(function (bodyText) {
console.log('Received the following instead of valid JSON:', bodyText); // 6
});
});
};
let fetchRes = fetch(
"https://jsonplaceholder.typicode.com/todos/1")
fetchRes.then(res =>
res.json()).then(data => {
console.log(data)
})
Your response is also correct but you can simplify using my code and for error handle you can write catch block.
You are saying that you are getting response in string but you should know that a object always have keys as a string , Even if you pass a number as a key to object , It will always go to string to object.
I think something like this could work out:
function GetFunding() {
fetch('my url',{
method: "POST"
})
.then(response => response.json())
.then(data => {
const items = data.items;
items.forEach(item => {
if (item.code === 'S') {
// do something
} else if (item.code === 'H') {
// do something else
}
});
})
.catch(error => console.error(error));
};
const string = '{ "items": [ { "number": 119777777, "code": "S", "name": "name Full award ", "year": 2021, "updatedate": "04-JAN-2023" }, { "number": 119777777, "code": "H", "name": "Hospital funding.", "year": 2021, "updatedate": "04-JAN-2023" } ] }';
const obj = JSON.parse(string);
const arrayOfObjects = obj.items;

Retrieve multidimensional JSON data where index number is randomized/unknown

Been delivered some confusing JSON data with a problem I haven't seen before.
The JSON is formatted similar to this structure:
[
{
"title": "Event",
"start_date": "2022-08-20 15:00:00",
"end_date": "2022-08-20 16:00:00",
"branch": {
"85": "branchname"
},
"room": {
"156": "roomname"
},
"age_group": {
"5": "Youth",
"6": "Teen"
}
},
{
"title": "Event02",
"start_date": "2022-08-20 15:00:00",
"end_date": "2022-08-20 16:00:00",
"branch": {
"72": "branchname"
},
"room": {
"104": "roomname02"
},
"age_group": {
"5": "Youth",
"6": "Teen"
}
}
]
I'm trying to pull roomname out of the data, but it's nested in an object that has a random index number. If I manually put in the index number, I can retrieve the data, but the number changes every entry.
If I can figure out how to retrieve the number and store it in a variable, then use it again, or just somehow wildcard to just show any child of any key under the parent node "room" it would work perfect, but I don't know of a way to do this in javascript.
I'm limited to vanilla javascript, no external libraries or jquery.
here is the code that will output correctly if I manually enter the index numbers, but it only works for a single entry.
<script>
const url = 'example.json';
fetch(url)
.then((response) => {
return response.json();
})
.then((json) => {
json.map(function(event) {
console.log(`${event.start_date}`);
console.log(`${event.title}`);
console.log(`${event.room[156]}`);
return element;
});
}, 80);
</script>
EDIT: Forgot to point out, there is always only 1 entry in the "room" tag, but it's index is randomized, so if you just select the room tag it returns undefined or invalid. If I could wildcard the index so it just tries them all, or somehow retrieve the index number and store it in a variable, it would fix the issue.
I think this will work:
Here as you don't know the key so, instead of just guessing, you can use Object.values(JSONObjName) to get the list/array of values in that json.
Here I'm also using optional chaining (?.) to handle the case when the json has no key value pairs.
<script>
const url = 'example.json';
fetch(url)
.then((response) => {
return response.json();
})
.then((json) => {
json.map(function(event) {
const roomName = Object.values(event.room)?.[0];
console.log(`${event.start_date}`);
console.log(`${event.title}`);
console.log(`${roomName}`);
return {...event, room: roomName};
});
}, 80);
</script>
As long as you always want the first key you can fetch it like this
room = event.room[Object.keys(event.room)[0]]
if you want to get just roomname, you could do Object.values(room)[0]
or if you want the index and value you could go for Object.entries(room)[0]
arr?.map(({ room }) => {
for(let [key, value] of Object.entries(room)) {
console.log('Random Key : ',key)
console.log('Roomname : ', value)
console.log('Using random key : ',room[key])
}
})
By this way you can find the value of room against the random key.
Or you can try this if it is more relevant to you.
arr.map(({ room }) => {
for(let key of Object.keys(room)) {
console.log('Random Key : ',key)
console.log('Using random key : ',room[key])
}
})
Since you may want to do this for branch as well, here's an alternative solution which uses the object key as a computed property name (aka "dynamic key") to get the value.
And since, in this example it's done more than once, I've added that to a function that you can call in the destructuring assignment.
const data=[{title:"Event",start_date:"2022-08-20 15:00:00",end_date:"2022-08-20 16:00:00",branch:{85:"branchname"},room:{156:"roomname"},age_group:{5:"Youth",6:"Teen"}},{title:"Event02",start_date:"2022-08-20 15:00:00",end_date:"2022-08-20 16:00:00",branch:{72:"branchname02"},room:{104:"roomname02"},age_group:{5:"Youth",6:"Teen"}}];
// Get first key from an object
function getKey(obj) {
return Object.keys(obj)[0];
}
const out = data.map(obj => {
// Destructure the object and call `getKey` with the
// object to get its only key, and use that
// as a computed property to get its value, which
// we then relabel e.g. `roomName`
const {
branch: { [getKey(obj.branch)]: branchName },
room: { [getKey(obj.room)]: roomName },
...rest
} = obj;
// Now just return a new object with your new keys/values
return { ...rest, branchName, roomName };
});
console.log(out);
Additional documentation
Rest parameters
Spread syntax

JSON Parse nested objects in React

I'm fetching data in React from a MySQL database. MySQL auto-escapes my values including nested objects. I'm using the .json() function on the top-level but i'm not able to use this on sub-levels, nor JSON.parse(response[0].data) will work.
What is the right way of doing this?
fetch(`http://localhost:3000/getQuiz${window.location.pathname}`, requestOptions)
.then(response => response.json())
.then(response => {
console.log(response)
// {
// "id": 1,
// "url": "asd2q13",
// "data": "{name: \"Marie\", answers:[\"1\", \"3\", \"0\"]}"
// }
console.log(typeof response)
// object
console.log(response[0].data)
// {name: "Marie", answers:["1", "3", "0"]}
console.log(typeof response[0].data)
// string
console.log(response[0].data.name)
// undefined
})
The response.data is not a valid JSON string. You can try:
const response = {
"id": 1,
"url": "asd2q13",
"data": "{name: \"Marie\", answers:[\"1\", \"3\", \"0\"]}"
}
console.log(eval('(' + response.data + ')'))
Or Better:
const response = {
"id": 1,
"url": "asd2q13",
"data": "{name: \"Marie\", answers:[\"1\", \"3\", \"0\"]}"
}
function looseJsonParse(obj) {
return Function('"use strict";return (' + obj + ')')();
}
console.log(looseJsonParse(response.data))
But,
Warning: Executing JavaScript from a string is an enormous security risk. It is far too easy for a bad actor to run arbitrary code when you use eval(). See Never use eval()!, below.
I suggest you serialize the data correctly on the backend. I think the MySQL database driver can do this. Also, see Parsing JSON (converting strings to JavaScript objects)
MySQL (MySQL2) for Node was the big problem here. It's suppose to serialize "automatically", but somehow it ends up all wrong.
If I do JSON.Stringify() explicitly for the nested part before storeing it in the database it works!
const sendToDatabase = () => {
let nested = JSON.stringify({ name: "Marie", answers: ["2", "1", "0"] })
let post = { url: "asd2q13", data: nested}
var query = connection.query(
"INSERT INTO users SET ? ", post,
function (error, results, fields) {
if (error) throw error;
}
);
console.log(query.sql);
};
Then I call this on the front end
console.log(JSON.parse(response[0].data).name)
// Marie (string)
console.log(JSON.parse(response[0].data).answers)
// ["2", "1", "0"] (array)
The raw output from this is
{"name":"Marie","answers":["2","1","0"]}
insted of
{name: \"Marie\", answers:[\"1\", \"3\", \"0\"]}

How to Iterate Data in vue.js

I am trying to iterate this data on vue inside the method function
{
"data": [
{
"id": 1,
"name": "Jack Daniels",
"mobile": "21223",
"start": "2021-02-25T09:16:21.000000Z",
"end": "2021-02-25T09:16:21.000000Z"
}
]
}
here is my code
async getEvents() {
try {
const response = await axios.get('/api/customers')
Object.entries(response).forEach(([key, value]) => {
console.log(`${key}:${value}`)
})
} catch (err) {
console.log(err)
}
}
and here is the output on
data:[object Object],[object Object]
any idea what went wrong and if there is better approach for iterating the data in vue.js
First of all it's not Vue question(axios/js tags).
You don't need Object.entries here (if you need to iterate data array).
Just use map for array and that's all.
const iteratedData = response.data.map((item, index) => {
console.log(`Index ${index}`);
console.log(item);
// Do what you need with data, your iteration
return item;
});
Code is fine. Here key is data and value is array of object. so to access value, here is a sample code
let response = {
"data":[{
"id":1,
"name":"Jack Daniels",
"mobile":"21223",
"start":"2021-02-25T09:16:21.000000Z",
"end":"2021-02-25T09:16:21.000000Z"
}]
};
Object.entries(response).forEach(([key,value])=>{
console.log(`${key}:${value[0].id}`)
})
Here value is an array so iterate value to get properties. It is just a sample to show logic.

Typescript/JS recursive promises

I'm learning Javascript and I am stuck using Promises.
I'm trying to make a tree like structure from API documentation, where a $ref key in the JSON is replaced with the API object that resides somewhere else in the file. This needs to happen fairly synchronous where I go through the keys of the API object, when I find a $ref it's looked up and replaced in the JSON.
e.g.
"apiStorageVersion": {
"description": "...",
"properties": {
"apiVersion": {
"description": "...",
"type": "string"
},
"kind": {
"description": "...",
"type": "string"
},
"metadata": {
"$ref": "#/definitions/apiMetaData",
"description": "..."
},
"spec": {
"$ref": "#/definitions/apiSpec",
"description": "..."
},
"status": {
"$ref": "#/definitions/apiStatus",
"description": "..."
}
}
}
I start of with this function that gets a list of API objects that I consider parents in a sense that these are more important objects in the API. Definitions is the content of the file and holds all the API objects.
function fillObjectTree(parents: string[]) {
console.log(parents);
Object.keys(definitions).map(apiTitle => {
// if the apiTitle is part of the parents list than we process it
if (parents.includes(apiTitle)) {
// Read the children and see if any references are part of the parent
let par = readChildren(definitions[apiTitle])
par.then(function (vals) {
// Do something with values
})
}
})
}
Next step is reading the properties of this API object and looking for $ref keys.
function readChildren(definition: { properties: any; }) {
return new Promise((resolve, reject) => {
// Get the properties of the definition
let props = definition.properties;
// Properties are not always present on an object.
if (props) {
Object.keys(props).map(propName => {
Object.keys(props[propName]).map(elem => {
if (elem.includes("$ref")) {
// locationURL = the full url for the reference
let locationURL: string = props[propName][elem];
// returns the needed value for the URL based on the regex
let partialURL: string = locationURL.match('(?<=(\#\/.*\/)).*')[0];
readReference(partialURL).then((body) => {
console.log(body);
delete definition.properties[propName];
definition.properties[propName] = body;
});
}
})
})
resolve(definition);
} else {
resolve(definition);
}
});
}
When a reference is found a second function is called that looks in the current file for this object.
function readReference(apiTitle: string) {
return new Promise((resolve, reject) => {
// Check all the definitions and find a match
Object.keys(definitions).map(apiDef => {
if (apiTitle === apiDef) {
readChildren(definitions[apiDef]).then((body) => {
resolve(body);
})
}
})
})
}
So what's going wrong?
Well the order of operations does not seem to match what I want to happen. The object is not replaced in the JSON and not waited for when executing. I rather not used await or async but keep it to baseline Promises if possible.
readChildren will execute synchronously until it gets to this block:
readReference(partialURL).then((body) => {
console.log(body);
delete definition.properties[propName];
definition.properties[propName] = body;
});
readReference will return a promise, so it gets the promise then schedules whatever is inside the then to happen at some time in then future. Then the function continues, eventually calling resolve(definition); and then falling out of the function. This happens BEFORE whatever is inside the then.
To make resolve(definition); happen after everything else, simply put it in the then block too.
Edit: the above solution doesn't handle the map.
Handling lists of asynchronous results:
const promises = list.map(element => {
return someAsyncFunction(element);
});
Promise.all(promises)
.then(results => {
... do stuff with the results
});
Btw, all of this get flattened out if you use the vastly superior async/await syntax. It becomes much easier to reason about the ordering.

Categories