Return not working and overcomplex code - javascript

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
);

Related

Sum all numbers in array until a certain value is reached

I'm writing a function and this is the last piece of the puzzle, which I thought would be easy to deal with, but after some unsuccessful tinkering with for and while loops, I tried looking it up online and still couldn't find an answer. I came across some obscure and complex solutions, but I think there should be a more straightforward way to solve this. For example, if I have an array
[1, 2, 3, 5, 7, 9]
And the argument is 10, the function should return 6 (1 + 2 + 3), because the sum of the values should be <= 10 (or whatever number is passed). If the argument is 4, the function should return 3 (1 + 2) and so on.
You can use a for loop:
const arg = 10
const arr = [1, 2, 3, 5, 7, 9]
let res = 0;
const calc = (arr, limit) => {
for (num of arr) {
if (num + res > limit) {
break;
}
res += num;
}
return res;
}
console.log(calc(arr, arg))
.reduce() each current value (cur) added to the accumulator (acc) and checked vs. the limit (max). If acc + cur is less than limit then return acc+ cur, otherwise return acc. Added .sort() if the array happens to be out of order, as per Spectric's comment.
const array = [1,2,3,5,6,8];
const mixed = [0,7,3,8,2,1];
const A = 10;
const B = 15;
const closest = (arr, max) => {
return arr.sort((a, b) => a - b).reduce((acc, cur) =>
(acc + cur) > max ? acc : (acc + cur));
}
console.log(closest(array, A));
console.log(closest(array, B));
console.log(closest(mixed, B));
One interesting approach is to realize that we want to do a fold (something like Array.prototype.reduce) but one which we can escape early. The answer from zer00ne does this by choosing to check the condition on every iteration, and just continually returning the accumulator each time the condition is not met. That's fine for many use-cases, but it would be nice to make it more explicit.
I know of two ways to do this. One is to have some signal value that we would return -- probably a symbol -- to say, "We're done, just return the accumulator." The downside is that this function now depends on that external signal value. It's not terrible, and often might be the right solution. It's not hard to write, and I'll leave it as an exercise.
The other technique is to require the callback to explicitly choose whether to continue the iteration or stop by supplying it with next and done functions. If we want to continue, we call next with the next accumulator value. If we're finished, we just call done. Here's a version of that:
const fold = (fn) => (a) => ([x, ...xs]) =>
x == undefined ? a : fn (a, x, (r) => fold (fn) (r) (xs), () => a)
const sumUpTo = (n) =>
fold ((a, x, next, done) => a + x > n ? done () : next (a + x)) (0)
console .log (sumUpTo (10) ([1, 2, 3, 5, 7, 9])) //=> 6
console .log (sumUpTo (4) ([1, 2, 3, 5, 7, 9])) //=> 3
console .log (sumUpTo (10) ([1, 2, 3, 4, 5, 6])) //=> 10
sumUpTo takes our total value and returns a function that takes a list of numbers. It does this by calling fold using a callback function, the initial value (0 for a sum), and eventually passing our list of numbers. That callback does the work we care about. Then fold repeatedly calls it until it runs out of values or done is called.
We can break down the one-liner version above to focus specifically on the callback, if it makes it more clear:
const callback = (n) => (a, x, next, done) =>
a + x > n
? done ()
: next (a + x)
const sumUpTo = (n) => fold (callback (n)) (0)
It's a very elegant pattern, to my mind.
Quickest way with smallest code footprint:
const dataset = [1, 2, 3, 5, 7, 9]
const getMaxSum = (dataset, max) => {
var sum = 0, num;
for( num of dataset ) {
if( sum + num > max ) break // very important to break once satisfied
sum += num
}
return sum
}
console.log( getMaxSum(dataset, 10) )
console.log( getMaxSum(dataset, 20) )

How to get the sum of all duplicates in an array?

I am trying to calculate the sum of all duplicates in an array. For example:
duplicate([1,1,2,3,3]) --> should return 8.
I have written the following function to calculate the sum of duplicates in an array using JavaScript. Currently it is returning an array with duplicates one less than what they are present in the array.
function duplicate(arr) {
var sum = 0;
arr.sort();
var stack = [];
for(var i = 0; i < arr.length; i++){
if (arr[i] === arr[i+1]) {
stack.push(arr[i])
}
}
return stack;
}
console.log(duplicate([1,2,1,2,2,3,3]))
This is returning [ 1, 2, 2, 3 ]
How do I get the correct array and calculate the correct sum? I have to use Object for that?
To make the logic easier, you might filter out the non-duplicates by checking whether their indexOf in the array is equal to their lastIndexOf in the array:
function duplicate(arr) {
const duplicates = arr.filter(elm => arr.indexOf(elm) !== arr.lastIndexOf(elm));
return duplicates.reduce((a, b) => a + b);
}
console.log(duplicate([1,1,2,3,3])); // --> should return 8.
console.log(duplicate([1,2,1,2,2,3,3]));
Initially create an object where the keys will be the integer and their value will be the number of occurrence. Then if the number of occurrence is more than 1 , multiply the number with number of occurrence.
function duplicate(arr) {
let dupVal = 0;
let k = arr.reduce((acc, curr, index) => {
if (acc[curr] === undefined) {
acc[curr] = 1
} else {
acc[curr] += 1;
}
return acc
}, {});
for (let keys in k) {
if (k[keys] > 1) {
dupVal += parseInt(keys, 10) * k[keys]
}
}
return dupVal;
}
console.log(duplicate([1, 2, 1, 2, 2, 3, 3]))
Try This one
const arr = [1,1,2,3,3]
let dup = arr.filter((value, index)=>{
// creating a copy of main array
let copyarr = [].concat(arr)
// removing present value
copyarr.splice(index,1)
// after removing present value, if you still
// get the value in copied array that means
// it has duplicates
if(copyarr.indexOf(value)>-1){
return true
}
return false
})
// now add it using reduce
let sum = dup.reduce((acc, value)=> acc+value,0)
console.log(sum)
Copy above code and paste into chrome devTool. You will get the answer.
The problem is that you are matching value with immediate next value in array, in array that is sorted already it will work, but not on unsorted one. So try to sort the array first and then run your code.
Edit :
Looks like sorting is added in code,
But another condition => if there is number that is repeated more than twice it should be handled and only appear once in stack, if that is required.
This will : console.log(duplicate([1,2,1,2,2,3,3]))
Result this : [1,2,3]
function duplicate(arr) {
var sum = 0;
arr.sort();
var stack = [];
for(var i = 0; i < arr.length; i++){
if (arr[i] === arr[i+1]) {
if(stack.length == 0 || (arr[i] != stack[stack.length-1])){
stack.push(arr[i])
}
}
}
return stack;
}
you can use JS Array.reduce method to accomplish your requirement in a shorter way
function sumDplicates(arr) {
return arr.reduce(function(tot, val, index, _arr) {
if (_arr.lastIndexOf(val) > index || _arr.indexOf(val) != index)
return tot + val;
return tot
}, 0)
}
console.log(sumDplicates([1, 1, 2, 3, 3]));
console.log(sumDplicates([1, 2, 1, 2, 2, 3, 3]));
You can pursue your original sorting approach with a slight modification:
if (arr[i] === arr[i + 1] || arr[i] === arr[i - 1])
That is, check if the previous or the next element in the sorted array is equal to the current element for it to qualify as a duplicate.
The following solution accomplishes this with filter and reduce:
function duplicate(array) {
return array
.sort((a, b) => a - b)
.filter((a, i, arr) => (arr[i] === arr[i + 1] || arr[i] === arr[i - 1]))
.reduce((a, b) => a + b, 0);
}
console.log(duplicate([1, 1, 2, 3, 3]));
console.log(duplicate([1, 2, 1, 2, 3, 3]));
Array.reduce() and Array.lastIndexOf() will simply solve your problem.
function sum(arr)
{
return arr.reduce(function(sum, item){
return arr.lastIndexOf(item)!==arr.indexOf(item) ? sum+=item : sum;
},0)
}
console.log(sum([1,1,2,3,3]));
console.log(sum([1,2,3,4]));
console.log(sum([1,2,2,3,4]));
console.log(sum([1,1,2,2,3,3,4,4]));
Though I don't know much about JavaScript, If I were you, I would have simply kept a temporary array, which copies all the duplicate variables and then use that array for sum.
Also, if you want to add the particular number as many times as it appears, I will suggest creating a table like the one in sparse matrices
and then referring to it during addition.
This logic, though not space efficient, is very easy to implement.
Here is an approach with a single Array.reduce and nothing else. No Array.indexOf or Array.lastIndexOf. Although it might not be as concise it does not traverse the array looking for indexes multiple times nor does any Array.filter:
const sumDubs = arr => arr.reduce((r,c) => {
if(r[c]) {
r[c] += 1
r.sum += r[c] > 2 ? (r[c]*c) - ((r[c]-1)*c) : r[c]*c
} else r[c] = 1
return r
}, { sum: 0 }).sum
console.log(sumDubs([1, 1, 2, 3, 3])) // 8
console.log(sumDubs([1, 2, 1, 2, 2, 3, 3])) // 14
console.log(sumDubs([1, 2, 1, 2, 2, 3, 3, 3, 1, 2, 4, 4])) // 28
The idea is to keep track of the on-going sum via a property in the accumulator of the Array.reduce and simply keep calculating the sum based on which number is duplicated and more importantly how many times.

Recursive version of find()

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

javascript, get indices of given value in array

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);}));

Map-Reduce : How to count in a collection

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);

Categories

Resources