Recreating JavaScript Reduce function part 2 - javascript

This in relation to the reduce function I recreated here, but a different question: Recreating JavaScript's reduce function.
I am reading Eloquent JavaScript and noticed that they recreated the reduce function a slightly different way with less code:
function reduce(array, combine, start) {
var current = start;
for (var i = 0; i < array.length; i++)
current = combine(current, array[i]);
return current;
}
console.log(reduce([1, 2, 3, 4], function(a, b) {
return a + b;
}, 0)); // → 10
I noticed that this only works when there is a start. For example, if I took away the start(0) and it was just:
console.log(reduce([1, 2, 3, 4], function(a, b) {
return a + b;
})); // NaN
It would return NaN. This doesn't make sense to me because the book says:
"If your array contains at least one element, you are allowed to leave off the start argument. The method will take the first element of the array as its start value and start reducing at the second element."
Only when I adjust it with an if statement does it produce "10" with or without a start(0).
function reduce(array, combine, start) {
var current = start;
for (var i = 0; i < array.length; i++)
if(current !==undefined){
current = combine(current, array[i]);
}else{
current=array[i];
}
return current;
}
What am I missing?

Here's how it should be, according to MDN (simplified version here):
function reduce(array, combine, start) {
var current = start;
var i = 0;
if (arguments.length < 2) {
while (array[i] === undefined) {
i ++;
if (array.length >= i) {
throw new Error('Empty array with no initial value');
}
}
current = array[i];
}
for (; i < array.length; ++ i) {
if (array[i] === undefined) continue;
if (current !== undefined) {
current = combine(current, array[i]);
} else {
current=array[i];
}
}
return current;
}

Related

How to return the first occurrence of repeated item in an array using for loop?

I am trying to build logic currently with arrays and data structure. I am trying to implement the logic using for loop
function getRepeatingNumber(arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
return arr[i];
}
}
}
return undefined;
}
getRepeatingNumber([2, 3, 6, 5, 2]);
the above function takes in array and returns a repeated item in the array so in the above case it will return 2. But what if I have an array something like this arr[2,3,3,6,5,2] in this case it should return 3 but as the outer loop has index [0] which is 2 as the reference it will return 2 as the answer.
How to implement a function that returns the first occurrence of the repeated item.
Instead of iterating with j in the part after i, iterate the part before i:
function getRepeatingNumber(arr){
for (var i = 1; i < arr.length; i++) {
for (var j = 0; j < i; j++) {
if (arr[i] === arr[j]) {
return arr[i];
}
}
}
}
console.log(getRepeatingNumber([2,3,3,6,5,2]));
Note that an explicit return undefined is not needed, that is the default behaviour already.
You could also use indexOf to shorten the code a bit:
function getRepeatingNumber(arr){
for (var i = 1; i < arr.length; i++) {
if (arr.indexOf(arr[i]) < i) {
return arr[i];
}
}
}
console.log(getRepeatingNumber([2,3,3,6,5,2]));
You could even decide to make use of find -- which will return undefined in case of no match (i.e. no duplicates in our case):
function getRepeatingNumber(arr){
return arr.find((a, i) => {
if (arr.indexOf(a) < i) {
return true;
}
});
}
console.log(getRepeatingNumber([2,3,3,6,5,2]));
If you do this for huge arrays, then it would become important to have a solution that runs with linear time complexity. In that case, a Set will be useful:
function getRepeatingNumber(arr){
var set = new Set;
return arr.find(a => {
if (set.has(a)) return true;
set.add(a);
});
}
console.log(getRepeatingNumber([2,3,3,6,5,2]));
And if you are into functions of functions, and one-liners, then:
const getRepeatingNumber = r=>(t=>r.find(a=>[t.has(a),t.add(a)][0]))(new Set);
console.log(getRepeatingNumber([2,3,3,6,5,2]));
You need a data structure to keep track of first occurring index.
My recommendation is to use an array to store all the index of repeating numbers. Sort the array in ascending order and return the item at first index from the array.
function getRepeatingNumber(arr){
var resultIndexArr = [];
var count = 0;
var flag = 0;
for(var i=0;i<arr.length;i++)
{
for(var j=i+1;j<arr.length;j++)
{
if(arr[i] === arr[j])
{
flag = 1;
resultIndexArr[count++] = j;
}
}
}
resultIndexArr.sort((a, b) => a - b);
var resultIndex = resultIndexArr[0];
if(flag === 1)
return arr[resultIndex];
else
return;
}
console.log(getRepeatingNumber([2,3,6,5,2])); // test case 1
console.log(getRepeatingNumber([2,3,3,6,5,2])); // test case 2
console.log(getRepeatingNumber([2,5,3,6,5,2])); // test case 3
This will return correct result, but this is not the best solution. The best solution is to store your items in an array, check for each iteration if the item already exists in your array, if it exists then just return that item.
as a javascript dev you should be comfortable wit functional programming & higher-order functions so check the doc to get more understanding of some useful functions: like filter - find - reduce - findIndex map ...
Documentation
Now to answer your question:
at first you should think by step :
Get the occurrence of an item in an array as function:
const arr = [2, 5, 6, 2, 4, 5, 6, 8, 2, 5, 2]
const res = arr.reduce((numberofOcc, item) => {
if (item === 2)
numberofOcc++
return numberofOcc
}, 0);
console.log(`result without function ${res}`);
/* so my function will be */
const occurenceFun = (num, arr) => {
return arr.reduce((numberofOcc, item) => {
if (item === num)
numberofOcc++
return numberofOcc
}, 0);
}
console.log(`result using my function ${occurenceFun(2, arr)}`);
Now i have this function so i can use it inside another function to get the higher occurrence i have in an array
const arr = [1, 2, 5, 6, 8, 7, 2, 2, 2, 10, 10, 2]
const occurenceFun = (num, arr) => {
return arr.reduce((numberofOcc, item) => {
if (item === num)
numberofOcc++
return numberofOcc
}, 0);
}
/*let's create our function*/
const maxOccurenceFun = arr => {
let max = 0;
arr.forEach(el => {
if (max < occurenceFun(el, arr)) {
max = el
}
})
return max;
}
console.log(`the max occurence in this array is : ${maxOccurenceFun(arr)}`);

Function call during loop is incorrectly returning undefined

New to js and I'm attempting to solve a really simple problem. I'm not sure why it's not working though. Must be a js quirk I am unfamiliar with. Can someone tell me why x would return back as undefined thus causing my function to return false when it should return true? I'm trying to replicate the 'every' method for arrays and return false if one of the array elements returns false from my callback.
I've attempted to debug with webstorm and I still could not find the solution. Here is my code,
function every_loop(array, test) {
for (let index = 0; array.length - 1; index++) {
let x = test(array[index]);
if (!x) {
console.log(array[index], index);
return false;
}
}
return true;
}
console.log(every_loop([1, 2, 3, 4, 5], n => n >= 1));
My output is false when it should be true. Also, right before it outputs false, it shows undefined as a value for array[index] which leads me to believe that my for loop parameters are incorrect but that isn't the case either. Any help would be appreciated. Thanks
you are missing the clause of the second parameter of the for loop.
changing it to index <= array.length - 1 fixes your code.
function every_loop(array, test) {
for (let index = 0; index <= array.length - 1; index++) {
let x = test(array[index]);
if (!x) {
console.log(array[index]);
return false;
}
}
return true;
}
console.log(every_loop([1, 2, 3, 4, 5], n => n >= 1));
Your for loop is broken. I would recommend for..of -
function everyLoop (arr, test) {
for (const x of arr)
if (!test(x))
return false
return true
}
console.log(everyLoop([1,2,3,4,5], n => n >= 1)) // true
console.log(everyLoop([1,2,3,4,5], n => n >= 2)) // false
You said it's practice, but just know JavaScript does include a built-in Array#every that does exactly this -
console.log([1,2,3,4,5].every(n => n >= 1)) // true
console.log([1,2,3,4,5].every(n => n >= 2)) // false
Your condition in for loop will be always true. (array.length-1==4). So use this instead:
function every_loop(array, test) {
for (let index = 0; index<array.length;index++) {
let x = test(array[index]);
if (!x) {
return false;
}
}
return true;
}
console.log(every_loop([1,2,3,4,5], n => n >= 1));
console.log(every_loop([0,1,2,3,4,5], n => n >= 1));
replace
for (let index = 0; array.length - 1; index++) {
with
for (let index = 0; index < array.length; index++) {

array function that generates array with subset range of numbers

I am trying to create a function that builds an array up to a number set by the function parameter, with an if condition on being included based on whether the remainder is zero. The last number in the array should be no higher than the parameter. Here's what I came up with so far --
function justThreesUpTo(num) {
var array = [];
array.length = num;
for (i = 1; i < array.length; i++) {
if(i % 3 === 0){
array.push(i);
}
else i;
}
array.splice(0, num);
return array;
}
When I console log this, with justThreesUpTo(20), I get --
// [ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42 ]
I see the issue being setting the limiter at array.length, which maxes out the number of items that can be in the array, but I can't figure out what else to call to make sure the last number in the array goes no higher than the "num" parameter specified by the function call. Any ideas?
Setting an array's length to something before the array is populated isn't a great idea - better to just iterate over the num itself. For example
for (var i = 1; i < num; i++) {
// push to array if i % 3 === 0
Your else i won't do anything - you can just leave it off completely.
You could make your code a whole lot shorter and cleaner if you wanted:
function justThreesUpTo(num) {
const length = Math.floor(num / 3);
return Array.from({ length }, (_, i) => (i + 1) * 3);
}
console.log(justThreesUpTo(20));
Modifying an array while looping over it (or its indices, which is what you’re doing with i < array.length) is a recipe for confusion. Start with an empty array and compare with num instead:
function justThreesUpTo(num) {
var array = [];
for (var i = 1; i < num; i++) {
if (i % 3 === 0) {
array.push(i);
}
}
return array;
}
Now you can optimize the check out of that entirely by moving up the appropriate amount each time.
function justThreesUpTo(num) {
var array = [];
for (var i = 3; i < num; i += 3) {
array.push(i);
}
return array;
}
(In your original code, the entire first num holes created by array.length = num; are unused and get spliced off, and else i does nothing.)
You can try with a simple while loop
function justThreesUpTo(num) {
var array = [];
var i = 0;
while (i < num) {
if(i % 3 === 0){
array.push(i);
}
i++;
}
return array;
}
console.log(justThreesUpTo(20));
You can use map method and spread syntax in order to write a clean solution.
function justThreesUpTo(num) {
return [ ...Array(Math.floor(num/3)).keys() ].map((_,i)=> (i+1) * 3);
}
console.log(justThreesUpTo(20));
Hmm. Looks like it was a pretty simple solution. Changed the limiter from "array.length" to "num", and it worked fine.
function justThreesUpTo(num) {
var array = [];
array.length = num;
for (i = 1; i < num; i++) {
if(i % 3 === 0){
array.push(i);
}
else i;
}
array.splice(0, num);
return array;
}
Never mind!
Use while with i+=3; inside the while loop:
function justThreesUpTo(num) {
var array = [];
var i = 0;
while(i<num){
array.push(i);
i+=3;
}
return array;
}
console.log(justThreesUpTo(20));

mergeSort algorithm and memory issue in JavaScript

Here's the code I wrote:
function mergeSort(array){
if(array.length < 2) return array;
var mid = Math.floor(array.length / 2);
var left = array.slice(0, mid);
var right = array.slice(mid, array.length);
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right){
var result = [];
while (left.length && right.length){
if(left[0]>right[0]){
result.push(right[0]);
} else {
result.push(left[0]);
}
}
while(left.length){
result.push(left[0]);
}
while(right.length){
result.push(right[0]);
}
return result;
}
array = [1000, -94, -115, 300, 22]
mergeSort(array);
and below is another solution i found online
function mergeSort (arr) {
if (arr.length < 2) return arr;
var mid = Math.floor(arr.length /2);
return merge(mergeSort(arr.slice(0,mid)), mergeSort(arr.slice(mid)));
}
function merge (a,b) {
var result = [];
while (a.length >0 && b.length >0)
result.push(a[0] < b[0]? a.shift() : b.shift());
return result.concat(a.length? a : b);
}
var test = [-100,3,53,21,4,0];
console.log(mergeSort(test));
in comparison I can't find any significant difference besides some syntax. But for some reason mine code won't run in both chrome dev console and node.js environment. In chrome, it won't return any result and in node.js it gives me
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory Abort trap: 6
Can someone help me understand what is the difference between the two snippet that actually made the difference?
Thanks in advance!
Think about it, you have an array left and you do
while(left.length){
result.push(left[0]);
}
left[0] doesn't change the array, it just gets the first item.
The length of left will never change, what you have there is a while loop that goes on as long as the array has a length over zero, and it always will, as the length never changes.
This is a perfect example of an infinite loop, that eventually fills the callstack and errors out, or in older browsers just crashes.
However if you do
while(left.length){
result.push(left.shift());
}
Array.shift() removes the first item from the array, so at some point the arrays length will be zero, and the loop stops
You never move from first element when you are doing merge. Try this code:
function mergeSort(array){
if(array.length < 2) return array;
var mid = Math.floor(array.length / 2);
var a = array.slice(0, mid);
var b = array.slice(mid);
return merge(mergeSort(a), mergeSort(b));
}
function merge(a, b){
var result = [];
var i = 0;
var j = 0;
while (i < a.length && j < b.length){
if(a[i] < b[j]){
result.push(a[i]);
i++;
} else {
result.push(b[j]);
j++;
}
}
while(i < a.length){
result.push(a[i]);
i++;
}
while(j < b.length){
result.push(b[j]);
j++
}
return result;
}
array = [1000, -94, -115, 300, 22];
array = mergeSort(array);
console.log(array);

How To Remove All Odd Numbers In An Array Using Javascript?

Can someone debug this code? I cannot for the life of me find the (run-time) error:
function generate_fibonacci(n1, n2, max, out){
var n = n1+n2;
if(n<max){
out.push(n);
generate_fibonacci(n2, n, max, out);
}
}
function generate_fibonacci_sequence(max){
var out = [1];
generate_fibonacci(0, 1, max, out);
return out;
}
function remove_odd_numbers(arr){
for (var i = 0; i < arr.length; i++) {
if(!(arr[i]%2==0)){
arr.splice(i, 1);
}
}
return arr;
}
function sum(array){
var total = 0;
for (var i = 0; i < array.length; i++) {
total+=array[i];
}
return total;
}
var fib_sq = generate_fibonacci_sequence(4000000);
console.log("Before: " + fib_sq);
remove_odd_numbers(fib_sq);
console.log("After: " + fib_sq);
console.log("WTH?: " + remove_odd_numbers([1,2,3,4,5,6,7,8,9]));
Output:
Before: 1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578
After: 1,2,5,8,21,34,89,144,377,610,1597,2584,6765,10946,28657,46368,121393,196418,514229,832040,2178309,3524578
WTH?: 2,4,6,8
[Finished in 0.3s]
I'm going crazy or something. For some reason, all odd numbers are not being removed. But as you can see at the end, it works perfectly. I have no idea what is going on.
The problem in the original code is that when you remove the first 1 at index 0, the array gets shifted; now arr[i] is contains the second 1; but you just step over it.
You need to use while instead of if here, or copy to a separate list. This is an example for splicing:
function remove_odd_numbers1(arr){
for (var i = 0; i < arr.length; i++) {
// here
while (arr[i] % 2) {
arr.splice(i, 1);
}
}
return arr;
}
But it will be slow though. Better to create a new array:
function remove_odd_numbers2(arr){
var rv = [];
for (var i = 0; i < arr.length; i++) {
if (! (arr[i] % 2)) {
rv.push(arr[i]);
}
}
return rv;
}
Generally the best algorithm however is to use the same array, if the original is not needed, so that no extra memory is required (though on javascript this is of a bit dubious value):
function remove_odd_numbers3(arr){
var out = 0;
for (var i = 0; i < arr.length; i++) {
if (! (arr[i] % 2)) {
arr[out++] = arr[i];
}
}
arr.length = out;
return arr;
}
Notice however that unlike the splice algorithm, this runs in O(n) time.
Also, the Array.prototype.filter() is not bad, being a builtin. It also creates a new array and thus is comparable to the 2.
I'm not sure about this, however I doubt using splice is efficient compared to creating a new array.
function remove_odd_numbers(arr) {
var notOdd = [],
i = 0,
len = arr.length,
num;
for (; i < len; i++) {
!((num = arr[i]) % 2) && notOdd.push(num);
}
return notOdd;
}
EDIT: You should probably use the native filter function, as suggested by #Jack. I leave this answer as a reference.
Here is a really simple, fast way to do it. Using your data, it only took 48ms to complete. Hope this helps..
function noOdds(values){
return values.filter(function (num) {
return num % 2 === 0;
});
}
Because splice() modifies the array, your index will be off in the next iteration; you need to either decrease the loop variable, use a while loop like Antti proposed or iterate backwards like Crazy Train mentioned.
That said, the use of splice() is awkward to work with because it modifies the array in-place. This functionality can be easily accomplished using a filter function as well:
function remove_odd_numbers(arr)
{
return arr.filter(function(value) {
return value % 2 == 0;
});
}
This creates and returns a new array with only the even values.
Given the recency of this function, check the compatibility section how to handle browsers IE < 9. Many popular libraries, such as jQuery, underscore, etc. take care of this for you.
Update
Instead of filtering the array afterwards, it would be more memory efficient to only add the even values as you perform the recursion:
function generate_fibonacci(previous, current, max, callback)
{
var next = previous + current;
if (next < max) {
callback(next);
generate_fibonacci(current, next, max, callback);
}
}
function generate_fibonacci_sequence(max, callback)
{
callback(1);
callback(1);
generate_fibonacci(1, 1, max, callback);
}
var out = [];
generate_fibonacci_sequence(4000000, function(value) {
if (value % 2 == 0) {
out.push(value);
}
});
Instead of passing the out array, I'm passing a function to be called whenever a new sequence value is generated; the filtering is done inside that callback.
ES6 version from "Tabetha Moe" answer
function noOdds(arr) {
return arr.filter(value => value % 2 === 0);
}

Categories

Resources