In regards to my previous question (code is still giving me trouble): React: Javascript assignment not updating object
Code Here https://codesandbox.io/s/github/nieroda/js_err
You can see the object I have on line 2. There is no mutation that occurs between 2 and 5 although the print output is different (as seen below) leading me to believe that the code is being executed out of order.
codeBreaker.js:108
1. console.log("BEFORE")
2. console.log(gameBoardCopy[currentRow])
3. console.log("END")
let copy = this.state.gameBoard[currentRow].slice()
4. console.log("Copy Start")
5. console.log(copy)
6. console.log("Before Assignment")
copy[4] = { numColorMatch: 2, numExactMatch: 2 }
7. console.log("After Assignment")
8. console.log(copy)
9. console.log("copy end")
Looking at the output
BEFORE
2.
0: "BlueViolet"
1: "BlueViolet"
2: "BlueViolet"
3: "BlueViolet"
4: {numColorMatch: 0, numExactMatch: 0}
END
Copy Start
5.
0: "BlueViolet"
1: "BlueViolet"
2: "BlueViolet"
3: "BlueViolet"
4: {numColorMatch: 2, numExactMatch: 2}
Before Assignment
After Assignment
0: "BlueViolet"
1: "BlueViolet"
2: "BlueViolet"
3: "BlueViolet"
4: {numColorMatch: 2, numExactMatch: 2}
copy end
I cant figure out what is causing this, tips appreciated. Thanks
console.log is actually an async method and that is most likely why you are seeing the execution "appear" out of order. Whenever you console.log an object, make sure to console.log(JSON.stringify(JSON.parse(value)));.
A better way to see execution order and its values is to add debugger statement. Try adding debugger; right above step 5 and walk through the code to see what the values actually are. I would imagine the values would be as you expect them to be. If not, stepping through the process using the debugger will tell you why.
Looks like you are unintentionally mutating the state of your component. You are not copying the object here. Javascript objects are passed by reference, which means when you directly assign an object like this to another variable, they both will modify the same data.
Instead of:
let copy = this.state.gameBoard[currentRow].slice()
call:
let copy = Object.assign({}, this.state.gameBoard[currentRow]);
If it is your intention to update the state of your component you should call the.setState({obj}).
If you have to deep clone an object, I would suggest deep copy functions from either lodash or underscore (or create your own: Objects in JS: deep copy).
Hope this helps,
Related
What would be the best way to create an element that maintains an increasing counter across the tree? Example:
function SubElement(){
return <div><Counter/> <Counter/></div>
}
<CounterSection>
This should output 1: <Counter/>
<div>
This should output 2: <Counter/>
This should output 3 4: <SubElement/>
</div>
This should output 5: <Counter/>
This should output 6 7: <SubElement/>
</CounterSection>
The counter component can not leak any implementation details to the rest of the code, eg. having to pass some sort of counting prop into the <Counter/>.
I'm currently working through freeCodeCamp's JS course.
One of the last problems asks you to create a recursive function that only accepts one argument n and creates an array that counts down from n to 1.
I was able to solve the problem using this code (SPOILERS IF YOU ARE ALSO WORKING ON THIS PROBLEM):
// Only change code below this line
function countdown(n) {
if (n < 1) {
return [];
} else {
const countArray = countdown(n - 1);
countArray.unshift(n);
return countArray;
}
}
// Only change code above this line
// my test
console.log(countdown(1))
I mostly arrived at this answer by copying syntax in the provided example. I plugged my answer into Python Tutor's code visualizer here. I will be referencing the steps in this visualizer.
Question about step 3: I notice it says countArray (block 1) is undefined. I assume this is because the function is hanging onto n and will go back and populate the array once the base statement creates it? Does this mean the defining of the array is delayed until the base case is reached?
Question on step 6: I see that my code worked as intended and now that n is 0, the base case is activated and the function returns an empty array. How does the code know that I want to populate this empty array with countArray? What ties the two together.
Question on step 7: If you can only answer one of my questions, I would like it to be this one.: Why does the function continue at all after the base case was reached (when n = 0)? From my flawed understanding return ends the function immediately. By this logic, my code shouldn't do what is intended. It would always count n down, and then regardless return an empty array.
Thank you for reading my question. If my thoughts are not detailed clearly enough here, please let me know how I can clarify.
Recursive function calls are stacked on top of the previous call.
So taking an example of countdown(2),
n > 0, i.e run else part which saves the value returned by countdown(1).
So now we go to countdown(1), which sends us to countdown(0).
Now, countdown(0) returns us an empty array.
From here, we run the left over part of countdown(1), i.e countArray = [] , which we got from countdown(0)
unshift means push value at start of array. So now our array is countArray=[1]
We return this value to countdown(2) (the first line). And now our countArray = [1] is in our initial function call.
Then we run the left over part again: countArray.unshift(2) -> [2, 1]
And finally we return it to whatever called it.
// Call Stack:
countdown(2) -> countdown(1) -> countdown(0)
// return Stack:
[2, 1] <- [1] <- []
Bonus: below is a one liner for the required function
const countdown = (n) => n ? [n, ...countdown(n - 1)] : []
console.log(countdown(5))
No array is created until you reach the base case.
When you call a function, a new context with its own local variables (called a stack frame) is created. Your visualizer shows these on the right ("Frames").
Notice how a new frame appears going from step 3 to 4. Then, when the base case returns going from step 6 to 7, the frame disappears again. The execution picks up where it left off before evaluating the base case, with the only difference being that the value for countArray is known.
return only finishes the current context, returning the intermediate result to its parent context. Basically, each level of recursion is independent here.
so I just came across this, but could anyone explain it to me, why is this phenomenon present here? I didn't find any help online...
1.So if I were to get the following two arrays:
let a = [];
let b = ['','',''];
2.and then push array b into array a three times:
a.push(b);
a.push(b);
a.push(b);
3.but then when I try to change the first element of the first array like so:
a[0][0] = 'foo';
4.then all the first element of ALL the arrays change, not just the first one, as console.log(a) outputs:
(3) [Array(3), Array(3), Array(3)]
0: Array(3)
0: "foo"
1: ""
2: ""
1: Array(3)
0: "foo"
1: ""
2: ""
2: Array(3)
0: "foo"
1: ""
2: ""
I would like to ask, why does this happen and in what way could I change an element based on its index inside of array a
b is a pointer to a mutable array, changing the array it points to will cause the other places you point to it to update as well.
You would either push copies of b into the array or create a modified copy of b when trying to mutate the value it points to.
Because you're passing b which is just a reference to an object. To have the behavior you want, change your code to be like this:
a.push(b.map((x) => x))
For more see this page.
if you console.log(b), you'll see that as well.
Try doing b[1] = "test" then check the value of a and you'll see all the b's that were pushed into there have test in them too.
b is a reference so anytime you change the value of it, you are altering the places it is referenced to.
When I try to console.log an object in Chrome, it states in the first line of the console.log (where you see Object { small summary here } that my posts array is of length 0 (posts: Array[0]).
However when I expand the post it shows that it has 27 items in it (which is what I need).
This happens to me randomly and I got no idea why it is happening, anybody experienced this before?
Screenshot:
Update: This happens in all browsers, so it is probably not chrome related
The debugger can't know if an object has been altered, this is why the posts attribute's rendering (in your example) has not been updated. Even if the debugger would be able to know when a attribute has been changed updating it every time (and all "loggings") would be too expensive.
So the debugger will check the attribute only when accessing it explicitly.
Chrome in this case will do this even only once:
p = []; window.x = {x: p};
Object {x: Array[0]}
x: Array[0]
__proto__: Object
x.x.push(1);
1
x.x.push(2);
2
Klicking x, the Array updates
p = []; window.x = {x: p};
Object {x: Array[2]}
x: Array[2]
0: 1
1: 2
length: 2
__proto__: Array[0]
__proto__: Object
Adding one item more to the array and toggleing x again, the size and entries remain
x.x.push(3)
3
x: Array[2]
0: 1
1: 2
length: 2
__proto__: Array[0]
In my opinion it's not necessary for the logger to update the object value since the variable watch has this function already. There you can always update the current value of a variable.
This works in Firebug and Chrome. Here's an example for Chrome:
The answer of "try-cache-finally" is correct too but I want to contribute the answer that caused the problem to happen in the first place.
when you have this code:
var b = [];
function a(callback) {
if (b.length > 0) {
return callback();
}
doXhr(function() {
b.push("something");
})
}
when you now call a twice then the second call will not see the updated a because of the XHR requests still being in process, this causes the xhr request to be called twice.
The solution for this is to wait on the callback for a like this:
a(function() {
a(function() {
// Correct trigger
});
});
So actually basic async programming, but this error was because of an already existing code base with this bug in it and it was hard to track down.
Background
Using JavaScript, I need to sort a large JSON object based on a given property of that object. I am assuming that a merge sort is the fastest approach. If this is not the fastest approach, please tell me what is. There are myriad examples online of a merge sort against an array, but very little with objects. Here is a sample object:
fruitForSale = {
1: {"type":"orange","UnitPrice":0.20},
2: {"type":"banana","UnitPrice":0.30},
3: {"type":"pear","UnitPrice":0.10},
4: {"type":"apple","UnitPrice":0.50},
5: {"type":"peach","UnitPrice":0.70}
}
Question
Using a merge sort (or faster algorithm), how would I sort the fruitForSale object so that I end up with an object sorted by 'type':
fruitForSale = {
4: {"type":"apple","UnitPrice":0.50},
2: {"type":"banana","UnitPrice":0.30},
1: {"type":"orange","UnitPrice":0.20},
5: {"type":"peach","UnitPrice":0.70},
3: {"type":"pear","UnitPrice":0.10}
}
NOTE: The original keys (1,2,3,4 & 5) would need to stay assigned to their respective object, so a key of 1 should always match with {"type":"orange","UnitPrice":0.20} and a key of 2 will always match with {"type":"banana","UnitPrice":0.30} and so on.
Thanks!
You can't sort the keys on the object, but you can keep your own array of sorted keys.
var fruitForSale = {
1: {"type":"orange","UnitPrice":0.20},
2: {"type":"banana","UnitPrice":0.30},
3: {"type":"pear","UnitPrice":0.10},
4: {"type":"apple","UnitPrice":0.50},
5: {"type":"peach","UnitPrice":0.70}
},
sortedKeys = Object.keys(fruitForSale).sort(function (i,j) {
return fruitForSale[i]["type"] > fruitForSale[j]["type"];
});
Example: http://jsfiddle.net/X2hFt/ (Output displayed on the console)
Object.keys is not supported everywhere but you can polyfill if you need to easily. See:
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/keys
Oh, and if you are curious about the underlying implementation of sort see:
Javascript Array.sort implementation?