i was traying to do the odin project exercises of javascript,
when I execute this line in the console
2 in [2,3,4,5]
it gives
true
but when I execute 3 in [3,4,5] in the console it gives back false I think it should also be true!!!
any explanation please
thank you in advance
The in operator checks if a property key exists on an object. You can think of this array as having 4 keys.
const arr = [2, 3, 4, 5];
// arr has the keys `0`, `1`, `2`, and `3`.
console.log(arr[0], arr[1], arr[2], arr[3]);
So when you have the expression 2 in arr, you are checking if the array has a key 2, not if the value 2 exists in the array.
If you do want to check if an array contains an item, the includes() method would achieve what you want.
const arr = [2, 3, 4, 5];
arr.includes(2); // true
arr.includes(0); // false
It behaves like this because the in operator returns true if the specified property (not value) is in the specified object.
So in the first case the array has a length of 4 with indices 0,1,2,3 so the element with the index 2 exists.
In the second case there are 3 elements with indices 0,1,2, so there is no element with an index 3 that is why it returns false.
Related
Why do these 2 expressions produce different results?
> [1,3,4].map(x=>5) // (3) [5, 5, 5]
> Array(3).map(x=>5) // (3) [empty × 3]
Because the arrays are different. [1, 3, 4] creates an array with a length of 3 and three entries in it. Array(3) creates an array with a length of 3 with no entries in it (a sparse array). map skips over empty slots in arrays, so your map callback is never executed.
You can see that here, where the first has a property called "0" (remember that standard arrays in JavaScript aren't really arrays at all¹, barring optimization in the JavaScript engine) but the second doesn't:
const a1 = [1, 3, 5];
console.log(a1.length); // 3
console.log("0" in a1); // true
const a2 = Array(3);
console.log(a2.length); // 3
console.log("0" in a2); // false
If you want to fill that array, use fill:
console.log(Array(3).fill(5));
fill is great where the same value is being used for all elements, but if you want different values for different elements, you can use the mapping callback of Array.from:
console.log(Array.from(Array(3), x => Math.random()));
When using Array.from, if the size is large (not just 3), then on some JavaScript engines (such as V8 in Chrome, Edge, and others), you'd want to use {length: value} rather than Array(value), because it's a temporary object and when you use Array(value), V8 (and perhaps others) pre-allocates storage for that number of elements, even though the elements start out initially empty. So:
console.log(Array.from({length: 3}, x => Math.random()));
¹ (That's a post on my anemic litle blog.)
[1,3,4].map(x=>5) // (3) [5, 5, 5]
The above can be re-written as
[1,3,4].map(function(x){
return 5;
})
Which will output [5,5,5] as it is returning 5 on every value in the array.
var a=Array(3).map(x=>5)
console.log(a)
The above creates a new array with 3 empty slots in array. Map skips all the empty elements and thus returns undefined
I'm trying to push a number to the end of an array without using the push function.
The array returns [1,2,3,10] but the array.length returns 8. I assume it's counting arr twice and the two numbers in 10 = 8. I don't know why.
function push(array, value){
arr=arr+','+value
return arr.length
}
var arr = [1, 2, 3]
console.log(push(arr, 10)) // expecting 4, get 8
+, when the left-hand side is not a number, will concatenate (possibly involving calling toString on the left-hand side first, like what's happening here). You can assign to the index at the length of the array instead:
function push(array, value) {
array[array.length] = value;
return array.length;
}
var arr = [1, 2, 3]
console.log(push(arr, 10))
var arr = [1,2,3];
arr[arr.length] = 4;
console.log(arr);
For completeness and because it hasn't been said yet you can also do this via array decomposition.
let array = [1, 2, 3];
array = [...array, 10];
console.log(array); // [1, 2, 3, 10]
Beware that this does write an entirely new array, but on the plus side it is immutable for the same reason!
Try running this in dev tools so you can see what exactly is happening. By adding a string to an array, you're converting it your original array to a string.
Not sure what you have against array.push() but I'd recommend using it.
Your code arr=arr+','+value is actually treating the array as a string. So the array starts as 1,2,3 (5 characters long) and then you add ,10 (3 more characters) to equal 8 characters.
If you really want to concatenate to the array without using JavaScript's built-in push() function, I'd suggest the splice() function as in this answer: How to insert an item into an array at a specific index (JavaScript)?
Please check the following...
var arr = [["test", 1], ["test", 3], ["test", 5]]
var otherArr = arr.slice(0) //should be a new array with a copy of arr
When i evaluate arr === otherArr the result is FALSE.
When i do the following, trying to change first array value:
otherArr[0][1] = otherArr[0][1] + 5;
it also changes the original array (arr)
arr[0][1] === otherArr[0][1] evaluates to TRUE
but arr === otherArr evaluates to FALSE
Please help me understand this to avoid it.
This is best explained visually. Imagine arr as the following diagram. It's actually composed of 3 arrays, one referencing the other two:
arr [ 0 , 1 ]
| |
v v
['test', 1], ['test', 5]
When you did otherArr.slice(0), it creates a "shallow copy" - a new array but with identical contents to the original array. That means arr and otherArr are two separate arrays, but point to the same contents. arr[0] is the same object as otherArr[0], and arr[1] is the same object as otherArr[1]
arr [ 0 , 1 ]
| |
v v
['test', 1], ['test', 5]
^ ^
| |
otherArr [ 0 , 1 ]
Now to your questions:
When i evaluate arr === otherArr the result is FALSE.
As mentioned above, arr and otherArr are two different arrays, causing === to fail. For non-primitives, === checks the identity (i.e. is it the same object?). Also note that === and == are NOT structural checks (i.e. do they look the same?).
When i do the following, trying to change first array value:
otherArr[0][1] = otherArr[0][1] + 5;
it also changes the original array (arr)
arr[0][1] === otherArr[0][1] evaluates to TRUE
Back to our diagram, what you're effectively doing is changing the contents of the object both arrays are referencing (in this case, the one that's now ['test', 6]).
arr [ 0 , 1 ]
| |
v v
['test', 6], ['test', 5]
^ ^
| |
otherArr [ 0 , 1 ]
This is because the arrays are different objects from the slice()
arr === otherArr // false (different objects)
But the values stored in the arrays are the the same objects
arr[0][1] === otherArr[0][1]
If you do not want this behavior, you will need to do a deep copy of the array instead of a slice.
Here is some information on deep copy as it relates to arrays: https://www.cs.utexas.edu/~scottm/cs307/handouts/deepCopying.htm
The problem is
The slice() method returns a shallow copy […]
You are replicating just the first level, so you have to go deeper to made a copy. You could do:
var arr = [["test", 1], ["test", 3], ["test", 5]]
var otherArr = [...arr[0], ...arr[1], ...arr[2]]
otherArr[0][1] = otherArr[0][1] + 5;
console.log(arr)
The JSON.parse(JSON.stringify(arr)) method is the best solution in general.
When i evaluate arr === otherArr the result is FALSE.
because a slice is not the same array.
it essentially is a copy of the arrays content.
When i do the following, trying to change first array value:
otherArr[0][1] = otherArr[0][1] + 5;
it also changes the original array (arr)
arr[0][1] === otherArr[0][1] evaluates to TRUE
but arr === otherArr evaluates to FALSE
yes because an array is a object and not a primitive value.
you are copying all the contents of arr into otherArr
but you are not creating copies of it's referenced objects.
if you had simple values in there like numbers, strings, boolean and probably even a regexp you could be sure that it would be copied.
if you access a sub array you need to create a slice of that sub array first too if you want an actual copy.
because arr is just pointing to the arrays. it does not contain them. they are references.
please also take a look at the comment from Calvin Nunes below your question:
you can create a deep copy by converting your array into a json string first and then back into an array:
otherArr = JSON.parse(JSON.stringify(arr))
keep in mind this works with simple stuff but if you want to copy objects that contain custom functions etc. you can forget about this.
they would not be added to the json representation and are lost.
json is just a format meant for transportation.
if you stumble across such a requirement in future you might want to consider rewriting your code instead.
this always leads to slow code.
you may want to recursively copy your array instead.
like.. walking through the array, if the member is of type array, walk through it too.
well.. there are various deep copy solutions out there. you just need to google for it.
So im learning about the different array iterating methods and ran into something odd:
[1,2,3].forEach(function(element,index,arr){
console.log(element,index);
console.log(arr);
arr.shift();
})
So you would think this would produce:
1,0
[1,2,3]
2,1
[2,3]
3,2
[3]
however you get this:
1 0
[1, 2, 3]
3 1
[2, 3]
Im printing off 3 despite have only shifted off at the end? Is this because the arr callback happens at the beginning of the next item or something? Since it should be doing shift() on the array AFTER? I realize this is a "bad case" of using this method, im just curious why this happens.
The .length of the array is changed. The element adjacent to element at index 0 becomes index 0 following .shift() call.
The answer to your question is described in the documentation of shift method.
As per MDN from here
The shift method removes the element at the zeroeth index and shifts
the values at consecutive indexes down, then returns the removed
value. If the length property is 0, undefined is returned.
So, once the shift operation is done the Original array is changed from
[1,2,3] to [2,3] and the value of index increments from 0 to 1. Next, iteration the third iteration of the loop is Not required as the length of the array (2) has been traversed.
While implementing an internal EventEmitter for a project I was working on, I came across a strange quirk when using Array.prototype.splice inside a for... in loop. The function does not successfully remove the indeces from the array within the loop:
var array = [1, 2, 3, 4, 5], index;
for (index in array) {
if (index === 2) {
array.splice(index, 1);
}
console.log(array[index]);
}
console.log(array);
Running on Google Chrome version 43, this outputs
1
2
3
4
5
[1, 2, 3, 4, 5]
when I'm expecting something like
1
2
4
5
undefined†
[1, 2, 4, 5]
Is this by design or a bug? I cannot find any documented reference to this behavior.
† Possibly, if length is not calculated during each iteration of for... in implementation
Great question. :)
In Javascript, Arrays are Objects, which means that Array indices are Object keys. And in Javascript, Object keys are strings.
So your condition index === 2 is always going to be false, because the number 2 is not the same as the string '2'.
One solution would be to continue to use the identity operator (recommended by most) and compare index to the string value '2'
index === '2'
Or, alternatively, you can use the equality operator which will typecast the comparison (although this will likely get you into trouble at some point)...
index == 2
but works just fine in your case.