Nested map function on parsed JSON data - javascript

I am receiving a JSON data from database which is
[{"table_cols":["id","stud_name","stud_school"]}].
console.log("Response is: " + resp._bodyText); {/*Response is: [{"table_cols":["id","stud_name","stud_school"]}]*/}
let parsedData = JSON.parse(resp._bodyText);
console.log(parsedData) //Object
this.setState({...this.state,cols_of_sel_tab:parsedData})
for(let i=0; i<this.state.cols_of_sel_tab.length;i++)
{
cols = [];
console.log(this.state.cols_of_sel_tab[i])
let cols = this.state.cols_of_sel_tab[i]
console.log("WOW")
for (let j = 0; j<cols.length; j++){
console.log(cols[j])
}
}
Output: {table_cols: Array(3)}
WOW
Expected: To iterate through the inner for loop
Desired output is: id, stud_name,stud_school
Tried: using forEach instead of inner for loop
Error: ...\node_modules\react-native\Libraries\ReactNative\YellowBox.js:82 Possible Unhandled Promise Rejection (id: 0):
TypeError: Cannot read property 'forEach' of undefined
TypeError: Cannot read property 'forEach' of undefined
I need help!

State updates are asynchronous. If you want to loop through the data you've received right then, use parsedData, not this.state.cols_of_sel_tab.
Separately, never do this:
this.setState({...this.state,cols_of_sel_tab:parsedData});
If you're setting state based on your current state, you must not pass in an object; instead, you must pass in a callback function that uses the state it receives as an argument (see the linked docs for details).
But in this case, you don't need the ...this.state at all, so it would be fine if you just did this:
this.setState({cols_of_sel_tab:parsedData});
...and then (again) looped through parsedData not this.state.cols_of_sel-tab.
Separately, you have a problem with cols: You're trying to use it in a block before it's declared with let. If you'd really run that code, it would have failed with an error saying cols is not defined (because you cannot access an identifier declared with let or const within a block prior to the declaration, even if it exists in an outer scope).

1. Solution to your question
The main problem why you (somehow without syntax error and not getting the empty state problem) get the
Output:{table_cols: Array(3)} WOW
and not the array items, because of the model of your response
[
{"table_cols":
["id",
"stud_name",
"stud_school"]
}
]
So, instead of
let cols = this.state.cols_of_sel_tab[i]
should be
let cols = this.state.cols_of_sel_tab[i].table_cols
And input will be:
{table_cols: Array(3)}
WOW
id
stud_name
stud_school
2. Syntax Error
cols = [];
let cols = this.state.cols_of_sel_tab[i]
You're trying to assign empty array to the variable, that not defined.
In this particular example you could just amend the first row
3. State handling
In your code example you've got two goals: update the state with new data and use the new data for output. Cause state updates are asynchronous is much safer to manipulate and output the data you already have right away (I amended debug console output).
this.setState({cols_of_sel_tab: parsedData})
for(let i=0; i<parsedData.length; i++) {
let cols = parsedData.cols_of_sel_tab[i].table_cols
for (let j = 0; j<cols.length; j++) {
console.log(cols[j])
}
}
Also, cause states updates are merged you can amend spread operator for this.state, like in the code above.
4.More improvements
There is a map function mention in header of the question, you could use it.
parsedData.map(
col_of_sel => col_of_sel.table_cols.map(
cols => console.log(cols)
)
)
or, if you only need the table_cols of the first object:
parsedData[0].table_cols.map(cols => console.log(cols))
You've got different code style in your example in question: semicolon presence, differences in indentations. I recommend to use some kind of prettier to pretty up the code, so it will be easier to read.
Finally
let parsedData = JSON.parse(resp._bodyText)
this.setState({ cols_of_sel_tab: parsedData })
parsedData[0].table_cols.map(cols => console.log(cols))

The problem is a slight misunderstanding of what your JSON data represents.
If we make the JSON a little prettier:
[
{"table_cols":
["id",
"stud_name",
"stud_school"]
}
]
The outer square brackets represent an array. This array contains one item at index 0, which is the table_cols object. This object also contains an array, with your table col fields in.
You're running a for loop on the outer array. The first item this loop will come across is the object at index 0. To reach the table columns you'd need to either run a second for loop on the table_cols, or run the original loop on this.state.cols_of_sel_tab[0].table_cols.

Related

Javascript 'for of' loop not properly resolving to the value

I have a simple array that I'm trying to iterate over but I'm apparently not understanding the 'for of' JavaScript loop. The following code returns exactly has it should;
const callOBJb = ['KD0NBH0BJ','W0DLKOBJ','WA0TJTOBJ'];
for (let i of callOBJb) {
console.log(i);
}
return: KD0NBHOBJ W0DLKOBJ WA0TJTOBJ
But the following code errors out with; "TypeError: i.getCenter is not a function. (In 'i.getCenter()', 'i.getCenter' is undefined)" because the variable 'i' does not resolve to one of the above.
for (let i of callOBJb) {
var Omiddle = i.getCenter();
}
When I manually type the variable in such as;
var Middle = W0DLKOBJ.getCenter();
It works just fine. What am I not understanding about how this should work?
I don't think I can use the ForEach here at least I'm not having any more luck than the for...of.
I was asked what the resolved variable for W0DLKOBJ might look like.
alert(JSON.stringify(KD0NBHOBJ));
{"_southWest":{"lat":39.204385,"lng":-94.60714},"_northEast":{"lat":39.20646,"lng":-94.60481}}
This works:
var Middle = W0DLKOBJ.getCenter();
because this:
W0DLKOBJ
is different from this:
'W0DLKOBJ'
If the array should contain the variable values and not string literals, don't use quotes:
const callOBJb = [KD0NBH0BJ, W0DLKOBJ, WA0TJTOBJ];
The callOBJb variable is an Array of strings, and when you use for..of statement to perform a loop iteration, for each item in your iterable object you will get item at deposition corresponding to the time to loop bloc code is execute.
In you case as all items in the callOBJb array is simple string, javascript String.prototype Object doesn't define a function named getCenter that is the reason why you are getting
"TypeError: i.getCenter is not a function. (In 'i.getCenter()', 'i.getCenter' is undefined)
let callOBJb = ['KD0NBH0BJ','W0DLKOBJ','WA0TJTOBJ'];
for(let item of callOBJb){
console.log(typeof item, item);
}
As you can see on each iteration the typeof item is always string.
I went back to the php and redesigned the output to make conversation to Javascript easier. By doing that I was able to iterate through the values using "for (let val of callOBJb)" successfully.
The solution came in how I received the data from PHP to start with. When I changed the definition to; const callOBJb = <?php echo "[$callOBJb]" ?> to include the square brackets and then used; for (let val of callOBJb) {...etc all of the variables resolved properly.

JavaScript Object giving different values when accessed directly

I am using React.js and I'm trying to update the state of my component when the props change. Before I explain the problem, I should mention that I have used both getDerivedStateFromProps and componentDidUpdate, the result is the same. The problem is that when I try to access the value of an element in prop, it differs whether I access the value directly or I use the object itself.
let userTickets = nextProps.support.userTickets;
// userTickets[0].messages is different from nextProps.support.userTickets[0].messages
below is the whole function code.
let userTickets = nextProps.support.userTickets;
console.log(nextProps.support.userTickets); // this contains the correct, updated value
for (let index = 0; index < userTickets.length; index++) {
let userTicket = userTickets[index];
console.log(userTicket); // this contains old incorrect value
}
Any guidance would be appreciated. Thanks.
Try to not directly assign props to variables because it may assign by reference instead of copy. Try this instead:
const userTickets = [ ...nextProps.support.userTickets ];
console.log(nextProps.support.userTickets);
userTickets.map(ticket => {
console.log(ticket);
});
Notice the 3 dot assignment of the array, this creates a new array with the values of the array that is "spread"

Arr.push inside For-loop

I have a SPA built with Vue and I'm trying to fetch an array of items from a custom API.
Once I've stored the items I'm trying to loop through that array and push in a custom path value, however I get the error
this.list[i].push is not a function
I'm not sure why this wouldn't be possible, but I'm sure I've missed something.
this.list: []
fetch(){
let url = 'http://core.com/api/v0/sources/'
axios.get(url).then((response) => {
this.list = response.data.data
for(var i = 0; i < this.list.length; i++){
let arr = { path: '/testPath' }
this.list[i].push(arr)
}
})
}
In the comments you mentioned your goal:
"this.list[i].path does not exist, that is what Im trying to create"
...if you literally want to add a property called "path" directly under the object at this.list[i] then you'd write
this.list[i].path = '/testPath'
This will create a new property for the object being held at this.list[i].
P.S.
You don't need your (badly-named) arr variable at all here.
Using .push() doesn't make any sense (and doesn't work) because this.list[i] contains an object rather than an array, and also doing that wouldn't create a property as you wanted.
push is the function of an array but you are trying to push an object in an object that's why you got this exception.
you can do as below.
this.list[i].path = '/testPath'
path property will be dynamically added in your object this.list[i].

creating objects in for loop javascript

I hope you can help me with this hopefully stupid problem.
I try to do the following:
creating array with data
looping through this array within a for loop (based on array.length)
create new object based on data in array
So far I got the following:
create array
loop through array
create one object based on my constructor
The problem is, the array has a length of 4 and should therefore create 4 objects but it creates only one. If I remove the creation of the object and just log "i' it works, but in the original intention it ends after the first
The loop looks as follows:
for(i=0;i<array.length;i++)
{
newObj[i]=new ObjectName(array[i].param1,array[i].param2,array[i].param3)
}
I have no idea why it ends after the first run and I also don't get an error displayed when looking into firebug.
Cheers
Does changing the
newObj[i] =
to
newObj.push(...)
help?
Also how is newObj initialized?
newObj = []
for (i = 0; i < (stringNums.length); i++) {
Dictionary[stringNums[i]] = stringNums[i].length;
}

How to nest loops to cycle through indexed array inside of associative array (JS)

I've got an indexed array of urls nested inside of another array that uses strings as its keys. I need to extract information from both arrays and I'm using the following loops.
// Loop through the elements in the associative level to get a count of its items
var keyCnt = 0;
for(key in serviceCategories) {
keyCnt++;
}
// Then loop through it again, this time nesting another for loop to get at the inner/indexed arrays
for(key in serviceCategories) {
var currCat = key;
for (var i = 0; i < keyCnt; i++) {
$.ajax({
url: serviceCategories[currCat][i],
success: function(data) {
parsePageCont (data, currCat);
}
});
}
}
}
This code works ok for the first element of the first array. It cycles through its inner array and excecutes the ajax call for every url with no problem. But then, when it finishes with the first element of the first array it doesn't proceed to the second one and fetch IT'S inner array data.
I hope that explanation wasnt too messed up.
You can see the full code here for more clarity: http://jsfiddle.net/FvP5f/
Assuming your data structure is an object with arrays for properties, you would do it like this.
serviceCategories has to be an object, not an array. Javascript doesn't have associative arrays. It has objects for storage by key and iteration by key. Arrays indexes are numeric.
You have to actually iterate over the length of each embedded array.
You can't refer to the key variable in the success handler because that gets called long after the loops so it's value has changed. To solve that problem, we put the key in a context object that will get set as the "this" pointer for the success handler so we can get back to the key.
Here's the code to solve those issues:
// assume data is of this structure (where I've shown numbers here, they are actually URLs)
serviceCategories = {
houses: [1,2,3,4],
cottages: [5,6,7,8],
hotels: [8,9,0,1],
apartments: [2,2,3,4,5,7,8,9]
};
for (key in serviceCategories) {
if (serviceCategories.hasOwnProperty(key)) {
var array = serviceCategories[key];
// got the next array, iterate through it
for (var i = 0; i < array.length; i++) {
var context = {};
$.ajax({
url: array[i],
context: {key: key}, // can't refer to key in success handler, so we sest it as context
success: function(data) {
parsePageCont(data, this.key);
}
}
});
}
}
Well, one problem seems to be that you are assuming that the second dimension of your array is always the same size (ie keyCnt). You also seem to be counting the wrong direction, by which i mean you would get two (inGoodHands and spaRituals), while you are using it for the second index which is 2 for inGoodHands, and 3 for spaRituals)).
It seems like you should be doing something like this:
for(x in serviceCategories) {
for(y in serviceCategories[x]) {
call ajax for serviceCategories[x][y]
}
}
Javascript scope is the function, not the block. While you can put var curcat inside a for loop it's important to understand that you are not creating a different variable for each iteration.
This means that all the closures you are creating in the loop will actually be based on the same variable so what you probably observed is not that only the first works, but that all of them worked by they were all working on the last key.
The solution for creating a local scope in javascript is to use a local function; instead of
(function(data){ parsePageCount(data, curcat); })
you could use
(function(x){return function(data){ parsePageCount(data, x); };})(curcat)
where the name x has been used instead of curcat to make clear the distinction.
This variable will be indeed separate for each of the created closures.

Categories