Say I have an array a = [8, 3, true, 9, false], and an indices array b = [1, 3, 4]. Is it possible to do something like this:
a[b] = false;
So that a becomes [8, false, true, false, false]?
That syntax won't work here, since b is an array, JS will try and convert b to a primitive by calling toString() on that array. Since b.toString() results in "1,3,4" you'll end up adding "1,3,4" as a property in your array:
const a = [8, 3, true, 9, false];
const b = [1, 3, 4];
a[b] = false; // adds property "1,3,4" to the array `a`, sets it to false
console.log(a["1,3,4"]); // false
You would need to manually loop through the indexes in b, and for each index set that index within a to false. This could be done with a regular for loop or forEach():
const a = [8, 3, true, 9, false];
const b = [1, 3, 4];
b.forEach(idx => a[idx] = false);
console.log(a); // [8, false, true, false, false]
If you want to keep this immutable and not modify the original array, one idea could be to use Object.assign() to merge and overwrite the indexes specified in b:
const a = [8, 3, true, 9, false];
const b = [1, 3, 4];
const res = Object.assign([], a, ...b.map(idx => ({[idx]: false})));
console.log(res);
There is no native support from the language but you can easily write a function for this.
const at = (ii, y) => (x, i) => ii.includes(i) ? y : x;
It is a curried function which takes a list of indexes ii and a new value y for these indexes. Then it returns a function that takes a value x and an index i. If i belongs to ii then returns new value y otherwise keep value x.
This makes it convenient to inline it when mapping an array:
[8, 3, true, 9, false].map(at([1, 3, 4], false));
// ^ ^ ^
// 1 3 4
//
//=> [8, false, true, false, false]
If you want to be able to assign different values at different indexes, then we can use the following data structure:
[[xi, xv], [yi, yv], [zi, zv], ...]
It represents a list of pairs where the first element represents the index and the second element represents the new value.
To support this we need to rewrite at.
For each (x, i) it tries to find a pair where the 1st el. is equal to i. If there's a result return the pair, otherwise return a temporary pair [, x]. Finally access the 2nd element of the pair which will be the value at that index.
const at = (...ys) => (x, i) => (ys.find(([j]) => j === i) || [, x])[1];
We can still inline it as above:
[8, 3, true, 9, false].map(at([1, 'x'], [3, 'y'], [4, 'z']));
// ^ ^ ^
// 1 3 4
// x y z
//
// [8, "x", true, "y", "z"]
Related
const arr = [1, 3, 1, 2, 5, 2, 3, 4, 1, 2, 3, 4, 3]
const resultado = arr.reduce((prev, cur) => ((prev[cur] = prev[cur] + 1 || 1), prev), {})
//resultado = const resultado = { 1: 3, 2: 3, 3: 4, 4: 2, 5: 1,}
I am new to javascript and I need to understand how, in the following code, the array ends in an object.
Create an empty object
For each element in the array:
Assign the value of [the object's value at thee current value plus 1, but if that doesn't exist yet return it the value of 1] to the key of the current index's value
Return the object so that it's available during the next iteration, and at the end for the final index
reduce works by passing in an initial "value" to the callback which acts as the "accumulator". The accumulator will be the first argument to the callback, and the current element in the array iteration will be the second. The accumulator is passed in on each iteration.
In this simple example we're just going to add up some numbers. We pass in 0 as the initial value. This will be passed into the callback as the "accumulator" (acc). c is the current element in the iteration.
On the first iteration acc is 0. On the second iteration it is 1 (0 + 1. On the third iteration it is 3 (1 + 2) - always adding on the value of the current element, and then being passed back into the callback until there are no more elements.
const arr = [1, 2, 3, 4, 5];
const out = arr.reduce((acc, c) => {
return acc + c;
}, 0);
console.log(out);
The example in your question follows the same logic but because it's all on one line it makes it more difficult to understand, so here it is expanded:
Instead of 0 being passed in as an initial value we're passing in an empty object.
If the object has a key that matches the value of the current element add 1 to that property value, otherwise initialise that property value to 1.
Return the accumulator for the next iteration.
const arr = [1, 3, 1, 2, 5, 2, 3, 4, 1, 2, 3, 4, 3];
const resultado = arr.reduce((acc, c) => {
if (acc[c]) {
++acc[c];
} else {
acc[c] = 1;
}
return acc;
}, {});
console.log(resultado);
I would like to find the indexes of all occurrences of an item in a list / an array, preferably using a PREDICATE.
I use the IONIC - ANGULAR framework and therefore in TYPESCRIPT.
Here is a concrete example of what I would like:
const myList = [0, 2, 1, 1, 3, 4, 1];
// what already exists:
myList.findIndex(x => x === 1); // return 2
// what I would like it to be:
myList.findAllIndexes(x => x === 1); // should return [2, 3, 6]
Thanks in advance for your help.
Use map and filter the undifened items in from the result.
const myList = [0, 2, 1, 1, 3, 4, 1];
const myIndexList = myList.map( (x,index) => { if(x === 1) return index}).filter(item => item !== undefined);
console.log("myIndexList: ", myIndexList);
SOLUTION :
/**
* Returns the indexes of all elements in the array where predicate is true, [] otherwise.
* #param array The source array to search in
* #param predicate find calls predicate once for each element of the array, in descending
* order, until it finds one where predicate returns true. If such an element is found,
* it is added to indexes and the functions continue..
*/
findAllIndexes<T>(array: Array<T>, predicate: (value: T, index: number, obj: T[]) => boolean): number[] {
const indexes = [];
let l = array.length;
while (l--) {
if (predicate(array[l], l, array)) {
indexes.push(l);
}
}
return indexes;
}
and to use it :
const myList = [0, 2, 1, 1, 3, 4, 1];
const indexes = this.findAllIndexes(myList, x => x === 1);
// return [6, 3, 2]
OTHER METHOD :
A little different but can be useful (allow to get all elements and not indexes) :
const myList = [0, 2, 1, 1, 3, 4, 1];
const allElements = myList.filter(x => x === 1);
PS : I chose to iterate the loop from the end to the beginning, it is possible to invert it to get [2, 3, 6] instead of [6, 3, 2].
Happy codding everyone !
It's not 100% clear to me how this piece of code works:
var a = [1, 2, 3];
[x, y, ...a ] = [0, ...a, 4];
// OUTPUT: [0, 1, 2, 3, 4]
I'm deconstructing the array a using the ... operator.
I am expecting that in second line a bunch of assignments will take place.
The x will be assigned to 0, y will be assigned to ...a (which passes the elements in the array a as individual values).
It's not clear to me, though, how the ...a get assigned to 4. In fact, JS throws an exception when doing:
...a = 4;
// Uncaught SyntaxError: Rest parameter may not have a default initializer
Why does this code output the modified array with the end 4, instead of throwing an exception? How does this work exactly?
It is executed like following
var a = [1, 2, 3];
[x, y, ...a ] = [0, ...a, 4];
[x, y, ...a ] = [0, 1, 2, 3, 4];
which means first value in RHS array is assigned to x, second value in RHS array is assigned to y and the remaining values are assigned to a.
Hence, value of x is 0, y is 1 and a is [2, 3, 4]
It's not clear to me, though, how the ...a get assigned to 4.
It's not.
Lets split things up a little:
On the right hand side of the assignment you are using an array literal with a spread element. The value of a is "flattened" into the new array.
Thus, [0, ...a, 4] is is equivalent to [0].concat(a, [4]). The result is a new array.
var a = [1, 2, 3];
console.log('spread element', [0, ...a, 4]);
console.log('concat', [0].concat(a, [4]));
On the left hand side you are using array destructuring with a rest element. [x, y, ...a ] means
assign the first value of the iterable to x
assign the second value of the iterable to y
assign the remaining values of the iterable as an array to a
These two are equivalent:
var a = [1,2,3,4];
var [x, y, ...z] = a;
console.log('destructuring', x, y, z);
var x = a[0];
var y = a[1];
var z = a.slice(2);
console.log('manual + slice', x, y, z);
Of course combining these two is perfectly fine. In an assignment, the left hand side doesn't care what how the right hand side is computed and vice versa.
What's confusing about your example is that you are using a again in the destructuring assignment, but that's the same as overriding the value of a with a new value. However the end result is
[0, ...a, 4] results in [0,1,2,3,4] therefor
x has value 0
y has value 1
a has value [2,3,4]
In fact, JS throws an exception when doing: ...a = 4;
The error message you are getting is strange. It should really be just a syntax error.
... by itself doesn't mean anything. It's not an operator, it's a punctuator (like ; or ,) that has a different meaning depending on the context it is used (and allowed).
See also What is SpreadElement in ECMAScript documentation? Is it the same as Spread operator at MDN?
...a is either equal to .slice(start, end) (left, destructuring) or to .concat(a) (right, spreading):
[0, ...a, 4]
is equal to:
[0].concat(a).concat([4]) // [0,1,2,3,4]
Whereas:
[x, y, ...a] = array
Is equal to:
x = array[0];
y = array[1];
a = array.slice(2);
In the first example, spread ie ... in LHS acts as gatherer whereas on the RHS it acts as spread/rest. IE you are assigning value to variable a when it is on LHS.
var a = [1, 2, 3];
[x, y, ...a ] = [0, ...a, 4];
console.log(a)
Let's go step by step:
Let's start with RHS. Doing [0, ...a, 4] will generate [0, 1, 2, 3, 4]. See for yourself:
var a = [1, 2, 3];
console.log([0, ...a, 4]);
Now, the LHS is the side where assignment is taking place. On RHS, imagine any variable with spread operator as an array ready to be assigned new values.
So, we are trying to assign [0, 1, 2, 3, 4] to a variable x, then to y and the rest to array a (in that order). So, array a will have whatever will be left after first two assignments (ie 2, 3, 4).
var a = [1, 2, 3];
// a will get overwritten
[x, y, ...a ] = [0, 1, 2, 3, 4];
// same as [x, y, ...a ] = [0, ...a, 4];
console.log(a);
Finally, coming to your last question: "It's not clear to me, though, how the ...a get assigned to 4? "
Answer: It is not. But if you do something like [...a] = [4], it will result in an array named a containing [4].
You can read more about spread syntax here (MDN) and here (YDKJS).
Is there an elegant, functional way to turn this array:
[ 1, 5, 9, 21 ]
into this
[ [1, 5], [5, 9], [9, 21] ]
I know I could forEach the array and collect the values to create a new array. Is there an elegant way to do that in _.lodash without using a forEach?
You could map a spliced array and check the index. If it is not zero, take the predecessor, otherwise the first element of the original array.
var array = [1, 5, 9, 21],
result = array.slice(1).map((a, i, aa) => [i ? aa[i - 1] : array[0], a]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
An even shorter version, as suggested by Bergi:
var array = [1, 5, 9, 21],
result = array.slice(1).map((a, i) => [array[i], a]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A fast approach using map would be:
const arr = [ 1, 5, 9, 21 ];
const grouped = arr.map((el, i) => [el, arr[i+1]]).slice(0, -1);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is easily done with array.reduce. What the following does is use an array as aggregator, skips the first item, then for each item after that pushes previous item and the current item as a pair to the array.
const arr = [ 1, 5, 9, 21 ];
const chunked = arr.reduce((p, c, i, a) => i === 0 ? p : (p.push([c, a[i-1]]), p), []);
console.log(chunked);
An expanded version would look like:
const arr = [1, 5, 9, 21];
const chunked = arr.reduce(function(previous, current, index, array) {
if(index === 0){
return previous;
} else {
previous.push([ current, array[index - 1]]);
return previous;
}
}, []);
console.log(chunked);
If you're willing to use another functional library 'ramda', aperture is the function you're looking for.
Example usage taken from the ramda docs:
R.aperture(2, [1, 2, 3, 4, 5]); //=> [[1, 2], [2, 3], [3, 4], [4, 5]]
R.aperture(3, [1, 2, 3, 4, 5]); //=> [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
R.aperture(7, [1, 2, 3, 4, 5]); //=> []
You may do as follows with just a sinle liner of .reduce() with no initial;
var arr = [ 1, 5, 9, 21 ],
pairs = arr.reduce((p,c,i) => i == 1 ? [[p,c]] : p.concat([[p[p.length-1][1],c]]));
console.log(pairs);
I'm sure there is an elegant way, programmatically, but, mathematically I can't help seeing that each new pair has an index difference of 1 from the original array.
If you (later) have the need to turn your array [ 1, 5, 9, 21, 33 ] into [ [1, 9], [5, 21], [9, 33] ], you can use the fact that the difference between the indices is 2.
If you create code for the index difference of 1, extending this would be easy.
Here's slide which has two parameters to control the size of the slice and how many elements are dropped between slices
slide differs from other answers here by giving you these control parameters. other answers here are limited to producing only a slices of 2, or incrementing the slice by 1 each time
// take :: (Int, [a]) -> [a]
const take = (n, xs) =>
xs.slice(0, n)
// drop :: (Int, [a]) -> [a]
const drop = (n, xs) =>
xs.slice(n)
// slice :: (Int, Int, [a]) -> [[a]]
const slide = (m, n, xs) =>
xs.length > m
? [take(m, xs), ...slide(m, n, drop(n, xs))]
: [xs]
const arr = [0,1,2,3,4,5,6]
// log helper improves readability of output in stack snippet
const log = x => console.log(JSON.stringify(x))
log(slide(1, 1, arr))
// [[0],[1],[2],[3],[4],[5],[6]]
log(slide(1, 2, arr))
// [[0],[2],[4],[6]]
log(slide(2, 1, arr))
// [[0,1],[1,2],[2,3],[3,4],[4,5],[5,6]]
log(slide(2, 2, arr))
// [[0,1],[2,3],[4,5],[6]]
log(slide(3, 1, arr))
// [[0,1,2],[1,2,3],[2,3,4],[3,4,5],[4,5,6]]
log(slide(3, 2, arr))
// [[0,1,2],[2,3,4],[4,5,6]]
log(slide(3, 3, arr))
// [[0,1,2],[3,4,5],[6]]
If for some reason you didn't want slide to include partial slices, (slices smaller than m), we could edit it as such
// slice :: (Int, Int, [a]) -> [[a]]
const slide = (m, n, xs) =>
xs.length > m
? [take(m, xs), ...slide(m, n, drop(n, xs))]
: [] // <- return [] instead of [xs]
log(slide(2, 2, arr))
// now prints: [[0,1],[2,3],[4,5]]
// instead of: [[0,1],[2,3],[4,5],[6]]
I noticed that the current solutions, in a way, all look ahead or behind (arr[i + 1] or arr[i - 1]).
It might be useful to also explore an approach that uses reduce and an additional array, defined within a function's closure, to store a to-be-completed partition.
Notes:
Not a one liner, but hopefully easy to understand
part doesn't have to be an array when working with only 2 items, but by using an array, we extend the method to work for n-sized sets of items
If you're not a fan of shift, you can use a combination of slice and redefine part, but I think it's safe here.
partitions with a length less than the required number of elements are not returned
const partition = partitionSize => arr => {
const part = [];
return arr.reduce((parts, x) => {
part.push(x);
if (part.length === partitionSize) {
parts.push(part.slice());
part.shift();
}
return parts;
}, []);
};
const makePairs = partition(2);
const makeTrios = partition(3);
const pairs = makePairs([1,2,3,4,5,6]);
const trios = makeTrios([1,2,3,4,5,6]);
console.log("partition(2)", JSON.stringify(pairs));
console.log("partition(3)", JSON.stringify(trios));
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]