Related
After reading the MDN doc with examples of Destructuring assignment, and there is an example like:
const [a, b, ...{ pop, push }] = [1, 2];
console.log(a, b); // 1 2
console.log(pop, push); // [Function pop] [Function push]
and it says: This allows you to simultaneously unpack the properties and indices of arrays.
I took a second thought, isn't it will be fascinating if I could get the length of the array by doing this assignment at the same time getting the indices values out, so I tried this in my console:
const arr = [1, 2]
const [x, y, ...{length}] = arr
console.log(x) // 1
console.log(y) // 2
console.log(length) // 0 | ??? isn't it should give me 2?
// how about this...
const arr = [1, 2]
const {length} = arr
console.log(length) // 2 | ok well, it did work this way, but why not the destructuring assignment?
So, does anybody know why the length did not got assigned with value 2 in the first destructuring assignment? or did I found a bug in JavaScript?
What this does
[a, b, ...something] = someArray
is it takes the rest of the elements (index 2 and after) from someArray and puts them into a single variable as an array.
Doing
...{ pop, push }
takes two properties from that array: Array.prototype.pop and Array.prototype.push. But there aren't any more elements, because the original array had only 2 items, so doing
...{ length }
will give you 0.
If the original array did have more items, you'd see a length. For example:
const arr = [1, 2, 3, 4, 5]
const [x, y, ...{length}] = arr;
console.log(length);
The first two items are put into x and y. The last three items are taken as an array and then the length property is destructured from that array - so the length is 3.
Either add the number of destructured items previously to the length to get the actual length
const arr = [1, 2]
const [x, y, ...{length}] = arr
const actualLength = length + 2; // because 2 items were destructured; x and y
console.log(x) // 1
console.log(y) // 2
console.log(actualLength);
Or use object destructuring syntax instead
const arr = [1, 2]
const { 0: x, 1: y, length } = arr;
console.log(x) // 1
console.log(y) // 2
console.log(length);
Or, more understandable at a glance than either of the above - just do const { length } = arr on a separate line.
Let's see what happens when you only take one element off
const arr = [1, 2]
const [x, ...{length}] = arr
console.log("x", x) // 1
console.log("length", length) // 1
Notice how the length is one, because you start with 2 elements, then get rid of 1, and 2 - 1 is 1.
Why does this happen? ... means "rest", so it's the rest of the properties.
const arr = [1, 2]
const [x, ...rest] = arr
console.log("x", x) // 1
console.log("rest", rest) // [2]
When you use { } you get a property off of the rest, so it's the same as saying
const arr = [1, 2]
const [x, ...rest] = arr
console.log("x", x) // 1
const {length} = rest;
console.log("rest", rest) // [2]
console.log("length", length) // 1
var arr1 = [1,2,3,4,5];
var arr2 = ["a","b","c","d","e"];
Let's assume that I want to create a list like
1 a
2 b
3 c
4 d
5 e
by using template literal.
let x;
x = document.createElement('li');
x.innerHTML += `<span>${<arr1 index>}</span> <span>${<arr2 index>}</span>`
How can I do that ? Can we use forEach for two arrays in same time ?
This would be more like flatten(zip(arr1, arr2)). There is no built-in zip though you can very easily make it and you can see Array.flat here: MDN: Array.flat.
const arr1 = [1, 2, 3, 4, 5];
const arr2 = ["a", "b", "c", "d", "e"];
const flatten = arr => arr.flat();
const zip = (a, b) => a.map((e, idx) => [e, b[idx]]);
const arr3 = flatten(zip(arr1, arr2));
console.log(arr3);
The answer is "kind of." What you can do is loop through one array with a forEach method, and use the optional argument index to get the value of the second array as well. Something like this:
var arr1 = [1,2,3,4,5];
var arr2 = ["a","b","c","d","e"];
arr1.forEach((value, index) => {
console.log(value);
console.log(arr2[index])
})
But if the data in the two arrays are at all related, you'd want to put the data in the same object, like this:
var arr = [
{
num: 1,
letter: "a"
},
{
num: 2,
letter: "b"
},
{
num: 3,
letter: "c"
}
];
arr.forEach(value => {
console.log(value.num);
console.log(value.letter);
})
Or you would want to use a regular for loop
You could simply use a for() loop instead:
const max = Math.max(arrA.length, arrB.length)
for (let i = 0; i < max; i++) {
const objA = arrA[i],
objB = arrB[i]
if ('undefined' !== typeof objA) {
console.log({ objA })
}
if ('undefined' !== typeof objB) {
console.log({ objB })
}
}
There is no real magic here. You use an index variable, and let it increment:
var arr1 = [1,2,3,4,5];
var arr2 = ["a","b","c","d","e"];
let ul = document.querySelector("ul");
for (let i = 0; i < arr1.length; i++) {
let li = document.createElement('li');
for (let val of [arr1[i], arr2[i]]) {
let span = document.createElement('span');
span.textContent = val;
li.appendChild(span);
}
ul.appendChild(li);
}
<ul></ul>
There are of course other ways to loop, like with forEach, but it comes down to the same principle.
BTW, don't use string literals (template literals) for combining HTML with content, as you might have < or & characters in the content, which really should be escaped. In some cases, not escaping those may lead to unexpected side effects. By creating the elements with createElement and assigning content to their textContent or innerText properties, you avoid those potential issues. Some libraries make it possible to do this with less code, in a more functional way.
As to the initial data: in object oriented languages, like JavaScript, it is better practice to put related values together in one object. In the example, 1 and "a" apparently have a connection, so -- if possible -- you should define the initial data structure as something like this:
var data = [
{ value: 1, name: "a" },
{ value: 2, name: "b" },
{ value: 3, name: "c" },
{ value: 4, name: "d" },
{ value: 5, name: "e" }
];
What is the easiest way to remove all elements from array that match specific string? For example:
array = [1,2,'deleted',4,5,'deleted',6,7];
I want to remove all 'deleted' from the array.
Simply use the Array.prototype.filter() function for obtain elements of a condition
var array = [1,2,'deleted',4,5,'deleted',6,7];
var newarr = array.filter(function(a){return a !== 'deleted'})
Update: ES6 Syntax
let array = [1,2,'deleted',4,5,'deleted',6,7]
let newarr = array.filter(a => a !== 'deleted')
If you have multiple strings to remove from main array, You can try this
// Your main array
var arr = [ '8','abc','b','c'];
// This array contains strings that needs to be removed from main array
var removeStr = [ 'abc' , '8'];
arr = arr.filter(function(val){
return (removeStr.indexOf(val) == -1 ? true : false)
})
console.log(arr);
// 'arr' Outputs to :
[ 'b', 'c' ]
OR
Better Performance(Using hash) , If strict type equality not required
// Your main array
var arr = [ '8','deleted','b','c'];
// This array contains strings that needs to be removed from main array
var removeStr = [ 'deleted' , '8'];
var removeObj = {}; // Use of hash will boost performance for larger arrays
removeStr.forEach( e => removeObj[e] = true);
var res = arr.filter(function(val){
return !removeObj[val]
})
console.log(res);
// 'arr' Outputs to :
[ 'b', 'c' ]
If you want the same array then you can use
var array = [1,2,'deleted',4,5,'deleted',6,7];
var index = "deleted";
for(var i = array.length - 1; i >= 0; i--) {
if(array[i] === index) {
array.splice(i, 1);
}
}
EXAMPLE 1
else you can use Array.prototype.filter which creates a new array with all elements that pass the test implemented by the provided function.
var arrayVal = [1,2,'deleted',4,5,'deleted',6,7];
function filterVal(value) {
return value !== 'deleted';
}
var filtered = arrayVal.filter(filterVal);
EXAMPLE 2
array = array.filter(function(s) {
return s !== 'deleted';
});
A canonical answer would probably look like this:
[10, 'deleted', 20, 'deleted'].filter(x => x !== 'deleted');
//=> [10, 20]
There's nothing unexpected here; any developers can read, understand and maintain this code. From that perspective this solution is great. I just want to offer some different perspectives.
Firstly I sometimes struggle with the semantic of filter when the condition is "reversed":
[2, 3, 2, 3].filter(x => x === 2);
[2, 3, 2, 3].filter(x => x !== 2);
This is a contrived example but I bet a few readers did pause for a nanosecond. These small cognitive bumps can be exhausting in the long run.
I personally wish there would be a reject method:
[2, 3, 2, 3].filter(x => x === 2);
[2, 3, 2, 3].reject(x => x === 2);
Secondly there's a lot of "machinery" in this expression x => x === 2: a function expression, a parameter and an equality check.
This could be abstracted away by using a curried function:
const eq =
x => y =>
x === y;
[2, 3, 2, 3].filter(eq(2));
//=> [2, 2]
We can see that eq(2) is the same as x => x === 2 just shorter and with added semantic.
Now let's build a reject function and use eq:
const reject =
(pred, xs) =>
xs.filter(x =>
pred(x) === false);
reject(eq(2), [2, 3, 2, 3]);
//=> [3, 3]
But what if we need to reject other things? Well we can build an either function that uses eq:
const either =
(...xs) => y =>
xs.some(eq(y));
reject(either(1, 2), [1, 3, 2, 3]);
//=> [3, 3]
Finally to answer your question:
reject(eq('deleted'), [1, 'deleted', 3]);
//=> [1, 3]
reject(either('deleted', 'removed'), [1, 'deleted', 3, 'removed', 5]);
//=> [1, 3, 5]
We could go further and remove based on different predicates e.g. remove if matches the string "delete" or is 0.
Let's build a eitherfn function that takes a list of predicates:
const eitherfn =
(...fn) => y =>
fn.some(f =>
f(y));
And now let's build a match function:
const match =
x => y =>
typeof y === 'string'
? y.includes(x)
: false;
Then:
reject(eitherfn(match('delete'), eq(0)), [0, 1, 'deleted', 3, 'will delete', 5])
// [1, 3, 5]
When I try to make a function to swap 2 arrays, the original arrays are left unaltered.
function swap(x, y) {
var temp = x; x = y; y = temp;
}
u=[1, 0];
v=[0, 1];
swap(u, v);
console.log(u);
console.log(v);
This results in u as [1, 0] and v as [0, 1]. The values haven't been changed after the function call to swap.
On the other hand, if I do this without a function call:
u=[1, 0];
v=[0, 1];
var temp = u;
u = v;
v = temp;
console.log(u);
console.log(v);
Then they're swapped correctly, with u as [0, 1] and v as [1, 0].
I thought Javascript arrays are passed by reference, not by value. Am I misunderstanding something here?
Javascript does not have the ability to pass a reference to the u and v variables themselves. So, no assignment to x or y in your swap() function will change what is assigned to u or v. Javascript passes a reference to the object that u and v hold. Thus, you can't change the u and v variables from within swap(). You can change the contents of the object that they point to and thus properties of the object that u and v point to can be modified.
Since I have a C/C++ background, I think of what Javascript does when passing objects as "pass by pointer". When you call swap(u, v), what is passed to the swap() function is a pointer to the array that u also points to. So, now you have two variables u and x both "pointing" at the same array. Thus, if you modify that actual array, then since u points at that same array, both will see the modification. But, nothing you do inside the swap() function can change what object u or v actually point to.
In Javascript, the only way to change what object the original variables point to is to make them properties of an object and pass the object like this:
function swap(obj, x, y) {
var temp = obj[x]; obj[x] = obj[y]; obj[y] = temp;
}
var container = {};
container.u = [1, 0];
container.v = [0, 1];
swap(container, "u", "v");
console.log(container.u);
console.log(container.v);
If you don't mind rewriting both arrays from scratch, you can copy all the contents of one array to a temporary, then copy one array over to the other and then copy the temporary array contents back to the first original. This is not very efficient and there is probably a better way to solve your original problem, but it can be done.
function swap(x, y) {
// remove all elements from x into a temporary array
var temp = x.splice(0, x.length);
// then copy y into x
x.push.apply(x, y);
// clear y, then copy temp into it
y.length = 0;
y.push.apply(y, temp);
}
Getting the terminology on these "reference/value" questions is tricky, but I will do my best.
What you have for your Object / Array variables are really just references. Unlike C++, saying "x = y" does not actually copy the object's variables over to a new memory location. Internally, it's just copying a pointer location over. The language does not have constructions to "automatically recreate" something like an object or array in a new instance; if for some reason, you want to maintain two copies of an array, you will need to explicitly create it then copy over values (ie, = []; or = new Array(); or a copying function like = oldArray.map(...))
A little code example that might conceptually help. These same rules apply between objects and arrays.
a = {}; // In case you haven't seen it, this is like shorthand of "new Object();"
b = {};
c = b;
console.log(a === b); // false
console.log(b === c); // true
b.property = "hello";
console.log(c.property) // hello
Just like Java, JavaScript is pass-by-value only. Assigning to local variables in a function never has any effect on anything outside the function.
They are passed by reference, but they are also assigned by reference. When you write x = y you aren't modifying either of the arrays, you're just making your local variable x refer to the same array as y.
If you want to swap the array contents, you have to modify the arrays themselves:
function swap(x,y) {
var temp = x.slice(0);
x.length = 0;
[].push.apply( x, y );
y.length = 0;
[].push.apply( y, temp );
}
Feb 2022 Solution to Swap 2 Entire Array Contents.
You can use destructuring to swap the 2 arrays:
let a = [ 1, 2, 3, 4 ];
let b = ['a','b','c','d'];
[a,b] = [b,a]; // swap
console.log(a); // ["a", "b", "c", "d"]
console.log(b); // [1, 2, 3, 4, 5]
let a = [ 1, 2, 3, 4 ];
let b = ['a','b','c','d'];
[a,b] = [b,a]; // swap
console.log(a); // ["a", "b", "c", "d"]
console.log(b); // [1, 2, 3, 4, 5]
To swap 2 arrays you may use
Version 1
let arrA = [1,2,3,4];
let arrB = ['Eve', 'Bar', 'Foo'];
let tempArr = [arrA, arrB]; // constructing new array
arrA = tempArr [1];
arrB = tempArr [0];
Version 1 (shorthanded)
let arrA = [1,2,3,4];
let arrB = ['Eve', 'Bar', 'Foo'];
// RHS : construction a new array
// LHS : destruction of array
[arrB, arrA ] = [arrA, arrB];
Version 2 (spread operator)
let arrA = [1,2,3,4];
let arrB = ['Eve', 'Bar', 'Foo'];
let arrC = [...arrB]
arrB = [...arrA]
arrA = [...arrC]
I've been using underscore.js for a week now and I really like it.
However, now I want to replace a single element in collection (vm.lists) with another element.
I tried doing the following:
_.each(vm.lists, function (el) {
if (el.id == list.id)
el = list;
});
and:
var l = _.findWhere(vm.lists, { id: list.id });
l = list;
But none of them changes the actual item in the collection. How would I do this properly?
You got pretty close. The issue is that you are only replacing the local variables (el and l) pointing to the value, and this does not modify the initial list. A simpler example:
var list = [1, 2, 3];
var v = list[1];
v = 7; // v will now point to a new value, but list will stay intact [1, 2, 3]
Same with objects:
var olist = [{id: 1, v: 2}, {id: 4, v: 6}];
var obj = olist[0];
obj = {id: 8, v: 10}; // changes what obj points to, but does not affect olist[0]
var obj2 = olist[0];
obj2.v = 777; // olist[0] still points to the same object but the object is modified
olist[0] = {id: 8, v: 10}; // changes what olist[0] points to
So basically you have two options:
a) change vm.lists[index] so that it points to a new object. You would need to get the index of the object in the list and do vm.lists[index] = newObject;. Note that some underscore predicates also provide you the index _.each(list, function (el, index) {});.
b) change the object that vm.lists[index] points to, but you would need to copy fields manually. For example, if your object is represented as {id: id, values: [1, 2, 3], anotherField: data}, you could copy the fields:
//el.id = list.id; // don't need this as you were searching by id
el.values = list.values;
el.anotherField = list.anotherField;
IMO the first option would be cleaner.
there is no need for underscore here:
for (var i = 0, l = vm.lists.length; i < l; i++)
{
var el = vm.lists[i];
if (el.id == list.id) {
vm.lists[i] = list;
break; // stop the for loop
}
}