This JavaScript code tries to get the indices of a given value 5 in a new array. Any idea how to do it in an elegant way. I cold use for loop but I was hoping to use map or reduce. thx.
console.log( [1, 2, 3, 5, 6, 5].map((y, i) => {
if (y === 5) return i
}))
// gives --> [undefined, undefined, undefined, 3, undefined, 5]
// expected [3,5]
Unfortunately map and reduce are not lazily-evaluated in JavaScript (i.e. they're not generators/iterators), but if you don't mind the double array allocation, you can do this:
var indices = [ 1, 2, 3, 5, 6, 5 ]
.map( ( e, i ) => e == 5 ? i : -1 )
.filter( e => e > -1 );
// indicies == [3,5]
Another approach, that's cheaper:
var indices = [];
[ 1, 2, 3, 5, 6, 5 ].forEach( (e, i) => e == 5 ? indices.push( i ) : null );
This takes advantage of the fact you can use a void expression inside the ?: ternary operator.
You can use reduce. The third argument of the callback is the index.
[1, 2, 3, 5, 6, 5].reduce((indexes, n, index) => {
if (n === 5) indexes.push(index)
return indexes
}, [])
You can either use map() or reduce().
reduce() will be able to do it in a single loop:
[1,2,3,4,5].reduce((result, num, index) => result.concat(num === 5 ? index : []), []);
Using concat() instead of push() is a tiny bit slower, but cleaner, and in reasonably small sets, the difference will be negligible.
map() will need filter() to remove extras:
[1,2,3,4,5].map((num, index) => num === 5 && index)
.filter(e => e !== false);
.filter(Boolean) is a clean, short-hand way of casting whatever value to a Boolean, which filter will then use to determine what it needs to do. num === 5 && index will be either false or the index. Another clean way to go through.
You can use reduce operator on your array as:
[1,2,3,5,6,5].reduce(function(a, e, i) {
if (e === 5)
a.push(i);
return a;
}, []);
You could run a map to test the value and write either a null (no match) or the position (match) then a filter to remove the nulls.
The map would look like this:
map( (value, index) => {
return value === 5 ? index : null;
})
Then you'd filter it like this:
filter( (value) => {
return value != null;
})
console.log( [1, 2, 3, 5, 6, 5].map((y, i) => {
if (y === 5) return i
}).filter((x) => {return /\d/.test(x);}));
Related
Here a code as an example:
const nums = [1, 1, 3, 2, 2, 2, 2, 2, 2, 2];
oddArr = arrNum.filter(function(num,index){
return num % 2 != 0
})
evenArr = arrNum.filter(function(num,index){
return num % 2 === 0
})
Here I would like to return the new array with the original index of each element that meet the condition. I tried placing a , after the condition (num % 2 === 0) but nothing
In case of looking for odds number, I would like to get an output like this (bold data refers to the index of that number in the original array:
[1,0,1,1,3,2]
Maybe get an object for each result would be better. Something like this:
[
{1,0},
{1,1},
{3,2}
]
I dont even know if its possible,but I wonder because this other code effectively works:
function array_odd_even_position(a) {
return a.filter((num,index) => console.log(num,index));
}
array_odd_even_position([1, 1, 3, 2, 2, 2, 2, 2, 2, 2])
filter by itself isn't helpful here, since it will always return the values of the array, never its indexes. However, this'll do:
oddIndexes = arrNum.map((_, idx) => idx).filter(idx => arrNum[idx] % 2)
First map your array to an array of indexes, then filter those. You could use reduce to do it in one iteration:
oddIndexes = arrNum.reduce((acc, n, idx) => {
if (n % 2) {
acc.push(idx);
}
return acc;
}, []);
Since filter can only filter elements and you need both filter and to modify returned values you could use reduce method and do the both in one go.
const nums = [1, 1, 3, 2, 2, 2, 2, 2, 2, 2];
const { odd, even } = nums.reduce((r, e, i) => {
let key = (e % 2 == 0) ? 'even' : 'odd';
r[key].push(i)
return r
}, { odd: [], even: [] })
console.log(odd)
console.log(even)
I want to return an array of indices of an array which has positive elements using pure functions in JS. EX:
pos([1,-4,0,5,2])
>>[0,2,3,4]
So, I tried:
arr.map(function (ele){
return arr.findIndex(ele => ele > 0);
})
But my output is :[1,1,1,1]
Can someone tell me what is wrong? I believe that my map function is correct but something is wrong with findIndex(). (Please don't tell me the code as it is a question of my assignment. Only need hints.)
map() isn't quite right because you won't have corresponding output for every element in the original array. reduce() is probably what you want in this case:
function pos(arr){
return arr.reduce((ret_arr, number, index) => {
if (number >= 0) ret_arr.push(index)
return ret_arr
}, [])
}
console.log(pos([1,-4,0,5,2]))
You could first map the index, if the value is positive or -1 and then filter the array for existing indices.
Array#map returns a new array with the index of the element or -1 if the element is smaller than zero.
Array#filter returns all values which are not -1. The short adding just one returns a truthy value which is a possible index.
function pos(array) {
return array // [1, -4, 0, 5, 2]
.map((v, i) => v < 0 ? -1 : i) // [0, -1, 2, 3, 4]
.filter(i => i + 1); // [0, 2, 3, 4]
}
console.log(pos([1, -4, 0, 5, 2]));
Your callback for mapping
function pos(arr) {
return arr.map(function (ele) {
return arr.findIndex(ele => ele > 0);
});
}
console.log(pos([1, -4, 0, 5, 2]));
takes no element, because the first ele variable is never used later. Then you return always the index of the first element of arr, which is 1. This value is greater as zero and the callback for find returns true abd find hands over the actual index.
Don't use findIndex. Basically you are looking for
function pos(arr) {
var res = [];
for (const [index, el] of arr.entries())
if (el > 0)
res.push(index)
return res;
}
If you want to do this using the higher-order array methods, you could do
function pos(arr) {
return arr.map((ele, index) => [ele, index])
.filter(([ele]) => ele > 0)
.map(([_, origIndex]) => origIndex);
}
#NinaScholz has a good approach as well, though I'd write it as
function pos(arr) {
return arr.map((ele, index) => ele > 0 ? index : null)
.filter(index => index !== null);
}
Try this:
[1,-4,0,5,2].reduce((acc,value,i) => value >= 0 ? [...acc,i] : acc,[]);
In any case where you're ding both filtering and ?something-else? reduce is always the best option.
For basic algorithms like this try returning a new array containing the previous values via spread.
const possitive = [];
const negative = [];
const array = [-1, -2, 0, 4, 1, 3, 2];
// Separate negative from possitive values
array.map(number =>
number >= 0 ? possitive.push(number) : negative.push(number)
);
// Sort accessing order
possitive.sort((a, b) => a - b);
negative.sort((a, b) => a - b);
// Join both arrays
const completed = possitive.concat(negative);
// Console log completed array
console.log(completed);
//Result
[0, 1, 2, 3, 4, -2, -1];
As part of a bigger function, I'm trying to simply run a check to see whether the values in an array are 'increasing' - e.g.:
var a = [1,2,3,4,5] // is increasing
var a = [1,4,6,7,36] // is increasing
var a = [1,6,3,6,5] // not increasing
It's increasing if the previous value a[previous] is less than a[next]. For some reason, the problem is it doesn't return -1 when it's not increasing. And, as I'm learning JavaScript, my code seems way more complex than it should be.
My Questions:
1. why does it not return -1 when a cannot increase?
2. why is my code seemingly so complex for such a simple test? am I missing something? (that is, if you think it's overcomplex, which often my code is)
What would be a better way of writing such a function? Should I put the 'test if already increased' part inside a separate function? If someone could also give hints about writing better, simpler, readable, drier code, that would be much appreciated :)
var a = [1,2,3,4,5]
var canIncrease = 0; // boolean
// test if already increased
for(i=0;i<a.length;i++) {
if((a[i] < a[i+1] && i !== a.length-1)||(a[i] > a[i-1] && i==a.length-1)) {
console.log('index ' + i + ' cannot increase');
} else {
console.log('index ' + i + ' can increase');
canIncrease = 1;
}
}
if (!canIncrease) {
console.log('array a cannot increase');
return -1;
} else {
console.log('would continue');
// continue with main function...
}
You can use every() and return true if the element is the last element OR smaller than the next element.
function func(arr) {
return arr.every((o, i, a) => (i + 1) === a.length || o < a[i + 1] );
}
console.log(func([1, 2, 3, 4, 5]));
console.log(func([1, 4, 6, 7, 36]));
console.log(func([1, 6, 3, 6, 5]));
Doc: every()
To answer your last question, this can be simplified to the following:
var a = [1,2,3,4,5]
var b = [1,4,6,7,36]
var c = [1,6,3,6,5]
function isIncreasing(arr) {
return arr.every((n, i) => i === 0 || n[i - 1] < n);
}
isIncreasing(a); //true
isIncreasing(b); //true
isIncreasing(c); //false
You can use the .reduce method to achieve the desired result:
The .reduce method applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.
on every element compare the current element curr with the saved one acc. If the current is greater and not false save it in acc for the next element. Else set acc to false.
At the end, if the array is in ascending acc will be an integer (the last element in the array, i.e. the maximum). Else acc will be false.
adding !! before the statement ensures the integers are converted to bools.
So the statement will look like:
!!a.reduce((acc, curr) => (acc && (curr >= acc)) ? curr : false)
ES6 syntax:
() => {} or () => are shorthand syntax to define functions (arrow functions)
condition ? expr1 : expr2 is a ternary operator, it's equivalent to if(condition) { expr1 } else { expr2 }
Here are some tests:
const isAscOrder = (a) => !!a.reduce((acc, curr) => (acc && (curr >= acc)) ? curr : false);
console.log(
isAscOrder([2, 2, 3, 4, 5]) // is increasing
);
console.log(
isAscOrder([1, 6, 3, 6, 5]) // is not increasing
);
console.log(
isAscOrder([2, 1, 2, 3, 4, 5]) // is not increasing
);
I've just noticed the previous function didn't work if the array contained a 0: for example: [0, 2, 2, 3, 4, 5]. The reason being that 0 is false. In order to solve this, we can use NaN instead of false:
!isNaN(a.reduce((acc, curr) => (!isNaN(acc) && (curr >= acc)) ? curr : NaN))
const isAscOrder = (a) => !isNaN(a.reduce((acc, curr) => (!isNaN(acc) && (curr >= acc)) ? curr : NaN));
console.log(
isAscOrder([0, 2, 2, 3, 4, 5]) // is increasing
);
console.log(
isAscOrder([1, 6, 3, 6, 5]) // is not increasing
);
console.log(
isAscOrder([2, 1, 2, 3, 4, 5]) // is not increasing
);
What i'm trying to do is make a recursive version of find, which takes an array and a test function and returns first element of an array, that passes the test. Lets look at the example:
function isEven(num) { return(num%2 == 0); }
var arr = [1, 3, 5, 4, 2];
function findRecursive(arr, func) {
var p = arr.shift();
if (func(p) == true)
return p;
else
findRecursive(arr, func);
}
findRecursive(arr, isEven);
By some reason i'm getting undefined. But if i change shift to pop on line 5, it correctly returns 2. What causes th problem?
You need to return the value of the recursive call, otherwise you return undefined, the standard return value of a function in Javascript.
} else {
return findRecursive(arr, func);
// ^^^
}
You may insert a check for the length of the array, if there is no more element to check. Then you could return undefined intentionally.
function isEven(num) { return num % 2 === 0; }
function findRight(array, fn) {
if (!array.length) {
return;
}
var p = array.pop();
return fn(p) ? p : findRight(array, fn);
}
console.log(findRight([1, 3, 5, 4, 2], isEven)); // 2
console.log(findRight([1, 3, 5], isEven)); // undefined
Recursion is a looping mechanism that was born in the context of functional programming; taking it out of that context only permits a crude understanding of how recursion is meant to be used
Recursion, when used with other functional programming practices like persistent (immutable) data types and pure functions, can be expressed beautifully as a pure expression
const find = (f, [x,...xs]) =>
x === undefined
? x
: f (x)
? x
: find (f, xs)
const isEven = x =>
(x & 1) === 0
console.log (find (isEven, [1, 3, 5, 4, 2])) // 4
console.log (find (isEven, [1, 3, 5, 7, 9])) // undefined
Be careful with recursion in JavaScript, tho – use a stack-safe looping mechanism to avoid blowing the stack on a large array
const recur = (...values) =>
({ type: recur, values })
const loop = f =>
{
let acc = f ()
while (acc && acc.type === recur)
acc = f (...acc.values)
return acc
}
const find = (f, xs) =>
loop ((i = 0) =>
i === xs.length
? undefined
: f (xs [i])
? xs [i]
: recur (i + 1))
const isEven = x =>
(x & 1) === 0
// [ 1, 2, 3, 4, ... 20000 ]
const numbers =
Array.from (Array (2e4), (x,n) => n + 1)
console.log (find (isEven, numbers)) // 2
// this would have blown the stack using the first version of `find`
// but it works just fine here, thanks to loop/recur
console.log (find (x => x < 0, numbers)) // undefined
Important edit : I can't use filter - the purpose is pedagogic.
I have an array in which I would want to count the number of its elements that verify a boolean, using only map and reduce.
Count of the array's size
I already wrote something that counts the array's size (ie. : the number of all of its elements), using reduce :
const array_numbers = [12, 15, 1, 1]; // Size : 4
console.log(
array_numbers.reduce((acc) => {
return acc + 1;
}, 0)
);
Count of the array's elements checking a boolean condition
Now I would want to count only the elements that verify a boolean condition. Thus, I must use map before reduce and the elements of the map's returned array will be only the good elements.
So I wrote this code but it doesn't work... Indeed, I put null when I encounter a not-good element (and null is counted as en element unfortunately).
NB : here, the boolean condition is "is the element even ? (%2 == 0)".
const array_numbers = [12, 15, 1, 1]; // Size : 4
console.log(
array_numbers.map((current_value) => {
if(current_value % 2 == 0) {
return current_value;
}
return null;
}).reduce((acc) => {
return acc + 1;
}, 0)
);
Array#filter the array and check the length:
const array_numbers = [12, 15, 1, 1];
const result = array_numbers.filter((n) => n % 2 === 0).length;
console.log(result);
Or count using Array#reduce:
const array_numbers = [12, 15, 1, 1, 4];
const result = array_numbers.reduce((r, n) => n % 2 ? r : r + 1, 0);
console.log(result);
Or if you must, you can use Array#map with Array#reduce:
const array_numbers = [12, 15, 1, 1, 4];
const result = array_numbers
.map((n) => n % 2 === 0 ? 1 : 0) // map to 1 or 0 according to the condition
.reduce((r, n) => r + n); // sum everything
console.log(result);
You can use Array.prototype.filter to filter the even numbers - and you don't need the reduce() function - you can use length of the array returned by the filter() function.
Or you can use reduce() method alone like below:
See demos below:
const array_numbers = [12, 15, 1, 1]; // Size : 4
// using filter
console.log(
array_numbers.filter((current_value) => {
return current_value % 2 == 0;
}).length
);
// using reduce
console.log(
array_numbers.reduce((prev, curr) => {
return curr % 2 == 0 ? prev + 1 : prev;
}, 0)
);
Since per your comment you must use reduce for some reason:
arr.reduce((acc, n) => { n % 2 ? acc + n : acc }, 0);
The map is unecessary.
As Jared Smith mentioned, you don't need to use map for this task.
Array.reduce() gets the current element as a second argument in the callback function which you can use to check if it satisfies your given condition.
So, again assuming that you must use either map and/or reduce:
const myArray = [1,2,3,4,5,6];
const condition = function(a) {
// let's say
return a %2 == 0;
}
let result = myArray.reduce((acc, val) => {
return condition(val) ? acc + 1 : acc
}, 0);
console.log(result);