Javascript variable assignment in nested for loop - javascript

I am pulling my hair out with this algorithm. The goal is to take number array as an input and output a string that has all sequential numbers displayed as a range. For example if my input were [1,2,4,6,7,8,] my output should be "1-2,4,6-8". My issue lies with the variable assignment in my nested for loop's if statement. IndexEn is overridden each time j increments and should ultimately exit the inside loop as the highest value. The problem is that when I try to call the variable outside of the for loop it passes each value of IndexEn instead of the maximum. I don't understand how IndexEn is able to be outside of the scope of the inner loop while the inner loop is still running? Can someone please help me fix and understand whats happening here?
function bkInd(arr){
var bookSt="";
var indexSt;
var indexEn;
for(var i =0;i<arr.length-1;i++){
if(arr[i+1] !== (arr[i]+1)) {
if(i===0 || (i>0 && arr[i]) !== (arr[i-1]+1) ){
bookSt+= arr[i]+",";
}
// check to see if number are sequential and SHOULD output starting index value - last value
}else{
for(var j=i+1;j<arr.length;j++){
var count=0;
if(arr[j]==(arr[i +count]+1)){
indexSt = arr[i];
indexEn = arr[j];
count+=1;
}
}
//concatenate string
//console.log(indexEn); for value of index
bookSt+= indexSt+"-"+indexEn+",";
}
}
return bookSt;
}
var bookList = [1,3,4,5,7,9,10,11];
document.write(bkInd(bookList));

I don't like nested looping (can be slow on large datasets) so I took a different approach, hope you don't mind:
var bkInd = function(arr) {
var result = [];
var seq = []; // 'Temporary' array
for(var i = 0; i < arr.length; i++) {
seq.push(arr[i])
if(arr[i] + 1 !== arr[i + 1]) {
result.push(seq.length > 1 ? seq[0] + '-' + seq.pop() : arr[i]);
seq = [];
}
}
return result.join(', ')
}
// => '1, 3-5, 7, 9-11'
It instead 'builds up' a temporary array of numbers (seq) as it loops through arr. If the next number is sequential, seq keeps the last number and the loop continues. If the next number is more than one above the current one, seq is pushed to result, but if seq is more than one index long it will concatenate these to a hyphenated string. After seq get pushed, it gets reset to an empty array. Finally, it joins the result array with commas.

Code is updated according to your requirement :
function bkInd(arr){
var bookSt="";
var indexSt;
var indexEn;
for(var i =0;i<arr.length-1;i++){
if(arr[i+1] !== (arr[i]+1)) {
if(i===0 || (i>0 && arr[i]) !== (arr[i-1]+1) ){
bookSt+= arr[i]+",";
}
// check to see if number are sequential and SHOULD output starting index value - last value
}else{
var count=1;
indexSt = arr[i];
for(var j=i;j<arr.length;j++){
if(arr[j]==(indexSt+count)){
indexEn = arr[j];
count+=1;
i++;
}
}
//concatenate string
//console.log(indexEn); for value of index
bookSt+= indexSt+"-"+indexEn+",";
}
}
return bookSt;
}
var bookList = [1,3,4,5,7,9,10,11];
console.log(bkInd(bookList));
Your inner for loop logic was incorrect. I have updated the code to meet the requirement.

Scope is the region of the code within which a variable is directly accessible.
In JS scope is defined by functions, blocks (i.e. { and }) for let, const and by catch blocks.
The following seems to work:
function runnify(arr) {
var runFrom;
return arr.reduce((p,c,i)=>{
if (c + 1 === arr[++i]) { // true if next item is sequential
if (runFrom == null ) { // We are at start of run
runFrom = c;
}
return p;
}
if (runFrom) { // We are at the end of a run
p.push(`${runFrom}-${c}`);
runFrom = null;
return p;
}
p.push(c); // Not sequential
return p;
}, []).join(',');
}
var seq = [1, 3, 4, 5, 7, 9, 10, 11];
document.write(runnify(seq));

Basically, you could collect all items with a check, if consecutive in an array and join it at the end, for consecutive elements with a dash and the rest with comma.
function consecutive(array) {
return array.reduce(function (r, a,i) {
var last = r[r.length - 1];
if (!i || last[last.length - 1] + 1 !== a) {
r.push([a]);
} else {
last[1] = a;
}
return r;
}, []).map(function (a) { return a.join('-'); }).join();
}
var array = [1, 3, 4, 5, 7, 9, 10, 11],
grouped = consecutive(array);
console.log(grouped);

Related

How do I find two numbers in an array that are equal to a target number

I'm looking to find two numbers in an array that are equal to a particular target number. I thought this would be a simple task using .filter but for some reason my code only works when I'm looking for a target number of 4 but doesn't work for anything else?
What am I missing here?
var numbers2 = [1,2,3,4];
var target = 3;
var found = numbers2.filter((num) => {
return (num + num) !== target;
});
console returns (4) [1,2,3,4] as opposed to 2[1,2].
var numbers = [1,4,3,2,6,8,12,1,1,1,2,3,4];
var target = 3;
var output = [];
// Use a set to remove duplicate numbers
numbers = [...new Set(numbers)]; // Only do this step if you dont want duplicates ( like 2+2 = 4 so if your target was for 2, would not show up in the list )
// Sort the numbers from lowest to highest
numbers.sort( (a,b) =>a-b);
// Get index of first number that matches the target or is greater than the target
let index;
for( let i =0; i < numbers.length; i++) {
if( numbers[i] >= target ) {
index = i;
break;
}
}
// Remove all numbers from the array starting at the previous index as these are not possible to add up with another number to the target
if( index ) {
numbers.splice(index, numbers.length - index );
}
// Loop through the remianing array to get first number
numbers.forEach( ( num1, index1) => {
// Loop through array again to get second number
numbers.forEach( (num2, index2) => {
// Check if number is same is same index as you dont want to add the same value to itself, then check if the 2 numbers equal the target number
if( index1 !== index2 && num1 + num2 === target ) {
// If number already exists in array dont duplicate otherwise add it to the array
if( output.indexOf( num1 ) == -1 ) {
output.push( num1);
}
// If number already exists in array dont duplicate otherwise add it to the array
if( output.indexOf( num2 ) == -1 ) {
output.push( num2);
}
}
});
});
console.log( output);
You could find the array location of your target number through using a array.forEach, array.indexOf(), array.find(), and array.findIndex():
let numbers2 = [1,2,3,4];
let target = 4;
//Using foreach
numbers2.forEach((item, index)=>{
if (item == target){
console.log("Found the target at array location "+index);
}
});
//Or through using indexOf():
console.log("Found the target at array location "+numbers2.indexOf(target));
//Or through using find():
const found = numbers2.find(element => element == target);
console.log("Found "+target+" in the array.");
//Or through findIndex():
const target1 = (a) => a == target;
console.log("Found the target at array location "+numbers2.findIndex(target1));
Assuming:
you only need one pair
[2,2] does not count when your target is 4 (as '2' only appears once in the array)
One way to go is:
let numbers = [1, 2, 3, 4]
let target = 4;
let output = [];
const N = numbers.length
outer: for (let i = 0; i < N; i++) {
for (let j = i + 1; j < N; j++) {
if (numbers[i] + numbers[j] === target) {
output.push(numbers[i], numbers[j])
break outer;
}
}
}
console.log(output); //[1,3]
Edit: even if you want more than one pair, it's easy to modify to get that effect (now the target is 5):
let numbers = [1, 2, 3, 4]
let target = 5;
let output = [];
const N = numbers.length
for (let i = 0; i < N; i++) {
for (let j = i + 1; j < N; j++) {
if (numbers[i] + numbers[j] === target) {
output.push([numbers[i], numbers[j]])
}
}
}
console.log(output); //[[1,4], [2,3]]
This is an ideal case for the humble for loop. Methods like .forEach() will always try to loop over all the elements in an array, but if we order the data before we start the search we can break early and eliminate a lot of searching.
Ergo...
var numbers = [1,2,3,4];
var target = 5;
var output = [];
// Handling ordered data is much faster than random data so we'll do this first
numbers.sort();
// We want to start the inner search part way up the array, and we also want
// the option to break so use conventional for loops.
for (let i = 0; i<numbers.length; i++) {
for (let j=i+1; j<numbers.length;j++) {
// If the total = target, store the numbers and break the inner loop since later numbers
// will all be too large
if ((numbers[i]+numbers[j])===target) {
output.push([numbers[i], numbers[j]]);
break;
}
}
// Stop searching the first loop once we reach halfway, since any subsequent result
// will already have been found.
if (numbers[i]>(target/2)) {
break;
}
}
console.log( output);
It makes very little sense to get an array of single numbers, because you'll get all the numbers except for the last one unless the array starts at zero or there are numbers skipped. So I've written a function that'll return an array of single numbers or an array of expressions (strings).
First, make a copy of the array:
const array = [1, 2, 3, 4]
const copy = array.slice(0);
Next, use .flatMap() for the first set of iterations:
array.flatMap(num => { // This is the outer loop of numbers
If the third parameter expression is undefined it will default to false. Then .filter() the copy array, the criteria being that the number from the outer loop plus the current number of the inner loop equals the target number AND the numbers cannot be identical.
copy.filter(n => n !== num && target === n + num);
/*
Iterations on the first iteration of outer loop
1 + 1, 1 + 2, 1 + 3,...
*/
If expression is true, then use .flatMap() to return an expression (string) of whatever equals the target number or an empty array (which returns as nothing since .flatMap() flattens it's returns by a level). If both numbers are identical an empty array will be returned.
copy.flatMap(n => n === num ? [] :
target === n + num ? `${n} + ${num}` :
[]
);
If expression is true half of the array is returned so that there isn't any reversed dupes (ex. 6+2 and 2+6)
let half = result.length / 2;
result = result.slice(0, half);
const log = data => console.log(JSON.stringify(data));
// [1, 2, 3,...10]
const array10 = [...new Array(10)].map((_, i) => i + 1);
// [0, 2, 4, 6,...18]
const arrayEven = [...new Array(10)].map((_, i) => i * 2);
function operands(array, target, expression = false) {
const copy = array.slice(0);
let result = array.flatMap(num => {
if (expression) {
return copy.flatMap((n, i) =>
num === n ? [] :
target === n + num ? `${n} + ${num}` :
[]
);
}
return copy.filter(n => n !== num && target === n + num);
});
if (expression) {
let half = result.length / 2;
result = result.slice(0, half);
}
return result;
}
// Return as an array of single numbers
log(array10);
log('3: '+operands(array10, 3));
log('8: '+operands(array10, 8));
log('5: '+operands(array10, 5));
log(arrayEven);
log('2: '+operands(arrayEven, 2));
log('8: '+operands(arrayEven, 8));
log('15: '+operands(arrayEven, 15));
log('=======================');
// Return as an array of expressions (string)
log(array10);
log('3: '+operands(array10, 3, true));
log('8: '+operands(array10, 8, true));
log('5: '+operands(array10, 5, true));
log(arrayEven);
log('2: '+operands(arrayEven, 2, true));
log('8: '+operands(arrayEven, 8, true));
log('15: '+operands(arrayEven, 15, true));

javascript function to find the second largest element in an array

I am completing the hackerrank's 10 days of javascript challenge. The question:
write a function to take an array as an argument and then return the second largest element in the array.
I have written the code but my code is returning the largest element and not the second largest as asked.
function getSecondLargest(nums) {
// Complete the function
var largest=nums[0];
for(let i=1;i<nums.length;++i)
{
if(nums[i]>largest)
largest=nums[i];
}
var large=nums[0];
for(let j=1;j<nums.length;++j)
{
if(large<nums[j]&&large<largest)
large=nums[j];
}
return large;
}
When input array nums={2,3,6,6,5} the result is coming 6 while expected output is 5. Please help and point out the errors in the function code below.
should not initialize large with first value var large=nums[0]; because it may appear the biggest value and won't work
should use nums[j]<largest instead of large<largest as mentioned above
I think don't need second loop as all checks can be done in first loop, and you can assign prev largest to large whenever you change it:
function getSecondLargest(nums) {
var largest = nums[0];
var large;
for (let i = 1; i < nums.length; ++i) {
if (nums[i] > largest) {
large = largest;
largest = nums[i];
} else if (nums[i] > large || typeof large === 'undefined') {
large = nums[i]
}
}
return large;
}
console.log(getSecondLargest([5,1-2,3]))
console.log(getSecondLargest([-5,1,-2,3]))
GET SECOND LARGEST
first, I create new array with unique values.
let arr = [...new Set(nums)];
second, sort value using built-in function .sort().
note : by default .sort() always sorts asciibetically, but for some testcase, it doesn't work. So, I put (a, b) => { return a - b } to make sure it will work properly.
arr = arr.sort((a, b) => { return a -b });
third, get the value from arr
let result = arr[arr.length - 2] || arr[0];
finally, return the result
return result
function getSecondLargest(nums) {
let arr = [...new Set(nums)];
//Javascript's array member method .sort( always sorts asciibetically.
arr = arr.sort((a, b) => { return a - b });
let result = arr[arr.length - 2] || arr[0];
return result
}
Just one minor change:
Use nums[j]<largest instead of large<largest in the second for loop
function getSecondLargest(nums) {
// Complete the function
var largest=nums[0];
for(let i=1;i<nums.length;++i)
{
if(nums[i]>largest)
largest=nums[i];
}
var large;
//To ensure that the selected number is not the largest
for(let j=0;j<nums.length;++j)
{
if (nums[j] !== largest){
large = nums[j];
break;
}
}
for(let j=1;j<nums.length;++j)
{
if(large<nums[j]&&nums[j]!=largest)
large=nums[j];
else
console.log(large)
}
return large;
}
var secondLargest = getSecondLargest([6,3,6,6,5]);
console.log("Second largest number", secondLargest);
If you want to avoid using library functions like #ifaruki suggests, this line
if(large<nums[j]&&large<largest)
should read
if (large<nums[j] && nums[j] < largest)
Sorting and picking the second or second-to-last value fails when there are duplicates of the highest value in the input array.
Another easiest logic is to remove duplicates from the array and sort.
let givenArray = [2, 3, 6, 6, 5];
let uniqueArray = [...new Set(givenArray)];
console.log("The second largets element is", uniqueArray.sort()[uniqueArray.length - 2]);
I know you had your question answered, just thought I would provide my solution for any future users looking into this.
You can use reduce to go through the array while remembering the two largest numbers so far.
You just make a simple reduction function:
function twoMax(two_max, candidate)
{
if (candidate > two_max[0]) return [candidate,two_max[0]];
else if (candidate > two_max[1]) return [two_max[0],candidate];
else return two_max;
}
And then you use it for example like this:
let my_array = [0,1,5,7,0,8,12];
let two_largest = my_array.reduce(twoMax,[-Infinity,-Infinity]);
let second_largest = two_largest[1];
This solution doesn't require sorting and goes through the array only once.
If you want to avoid using **sort method. I think here's the easiest logic to do that, which will also work in arrays where there's duplicates of largest integer exists.
function getSecondLargest(arr) {
const largest = Math.max.apply(null, arr);
for (let i = 0; i < arr.length; i++) {
if (largest === arr[i]) {
arr[i] = -Infinity;
}
}
return Math.max.apply(null, arr);
}
console.log(getSecondLargest([5, 7, 11, 11, 11])); //7

refactorized solve for an algorithm in javascript

I have attended a technical interview for a development company. They asked me the following:
Giving an array of numbers (n) find 2 numbers that sum gives (k) and print them.
e.g
Input: n = [2,6,4,5,7,1], k = 8
Output: result=(2,6),(7,1)
My solution:
function findSum(n,k){
let aux = []
for (let i = 0; i < n.length; i++) {
for (let j = i+1; j < n.length; j++) {
if (n[i] + n[j] == k) {
aux.push({ first: n[i], second: n[j] })
}
}
}
return aux;
}
They told me that, it is possible to make the exercise with some kind of key or mapping.
Does some one know how to do it with only one loop?
The key to solving a question like this with low time complexity is the ability to efficiently search the data structure. A lot of answers rearrange the array in a way where searching an array is optimized. Another approach is with a data structure that inherently has fast search.
Set and Map data structures have O(1) time complexity for searches, which make them good data structures where searching can be leveraged to increase performance.
I use a new Map and traverse the array while adding it as a key. I set the key to the number and the value to the number of times I see it. I use a map over a new Set because I can also keep track of the number of instances of that particular number.
I search for the number that would sum up to k, which is: (k - num). If I find that number, I add both numbers to my results data structure and decrement the value by 1, to show that it's been used.
Time complexity: O(n), memory complexity: O(2n). Twice the amount of space compared to the original array because I have a key and a value to store in my Map
function pairSums(arr, k){
const map = new Map
const matches = []
for (let num of arr) {
const search = k - num
if (map.get(search) > 0) {
matches.push([num, k - num])
map.set(search, map.get(search) - 1)
} else if (!map.has(num)){
map.set(num, 1)
} else {
map.set(num, map.get(num) + 1)
}
}
return matches
}
console.log(pairSums([2, 6, 6, 6, 2, 4, 4, 4, 5, 7, 1, 4, 2], 8))
Match a number x from array with a key Math.min(x, k - x). Then run through your array and store every number in a hash using mentioned key. When the key you are going to add already is in the hash - check if stored value and current number gives required sum.
function findSum(n, k){
let hash = {};
for(let i = 0; i < n.length; ++i){
let x = n[i], key = Math.min(x, k - x);
if((key in hash) && hash[key] + x == k)
return [hash[key], x];
else hash[key] = x;
}
}
A task like this can be as simple or as complicated as you want to make it. Here's one solution, for example:
function findPairs(n, k) {
return n.reduce((pairs, next, i) => pairs.concat(
n.slice(i + 1)
.filter(num => next + num === k)
.map(num => [ next, num ])
),
[]
);
}
For the inputs [2, 6, 4, 5, 7, 1] and 8 will output [ [2, 6], [7, 1] ].
From https://www.geeksforgeeks.org/write-a-c-program-that-given-a-set-a-of-n-numbers-and-another-number-x-determines-whether-or-not-there-exist-two-elements-in-s-whose-sum-is-exactly-x/:
Sort the array in non-decreasing order.
Initialize two index variables to find the candidate elements in the sorted array. Initialize l to the leftmost index: l = 0, Initialize r to the rightmost index: r = n.length - 1
Loop while l < r.
if (n[l] + n[r] == k) then return 1
else if( n[l] + n[r] < k ) then l++
else r--
No candidates in whole array - return 0
I think by sorting you can do that
var n = [2,6,4,5,7,1];
var k = 8 ;
n.sort();
let start = 0, end = n.length-1;
while(start < n.length && end >= 0) {
let current_sum = (n[start] + n[end]);
if(current_sum == k) {
console.log('Found sum with '+ n[start] +' and '+ n[end]);
break;
}
else if(current_sum > k) {
end--;
} else {
start++;
}
}
if(start == n.length || end < 0) {
console.log('Not Found');
}
but while writing this code I got one another approach also
const set = new Set([2,6,4,5,7,1]);
var k = 8;
let found = false;
for (let item of set) {
let another = k - item;
if(set.has(another)){
console.log('found with '+item +' and ' +another);
found = true;
break;
}
}
if(!found) {
console.log('Not found');
}
If numbers are non-negative and the target value is within JavaScript array limit:
function findsums(arr,k){
var ret=[];
var aux=[];
arr.forEach(function(i){
if(i<=k){
if(aux[k-i])
ret.push([k-i,i]);
aux[i]=true;
}
});
return ret;
}
console.log(findsums([2,6,4,5,7,1],8));
Similar approach could work with a bitfield (or even with a sparse array of bitfields) too.
Minified alternative similar to #Andrew's great answer, but assumes that all numbers are above 0 :
var pairs = (arr, k) => arr.reduce((a, n) =>
(a[n - k]-- ? a.push([n, k - n]) : a[-n] = a[-n] | 0 + 1, a), []);
console.log(JSON.stringify( pairs([2,6,4,5,7,1], 8) ));

How do I find all 2 pairs of integers that have the same product in JavaScript?

I need to write a program that, when given a list of integers, it finds all 2-pairs of integers that have the same product. i.e. a 2-pair is 2 distinct pairs of integers lets say [(a,b),(c,d)] where a*b = c*d but a ≠ b ≠ c ≠ d.
The range of integers should be from 1 to 1024. What I would like to implement is that when the web page is opened the user is prompted by a pop up in which he will enter the array of integers, i.e [1,2,3,7,8,9,6] etc for instance from the input [1,2,3,7,8,9,6] the output should be [(9,2),(3,6)] since both evaluate to 18.
The coding I did so far is very basic and can be seen below. What I've done so far is the pop-up box alert the input etc, but can't seem to understand how to make the program check for the pairs and give the sum. Thanks in advance to this community who's helping me out to better understand and learn javascript!
I've done my fair bit of research below, definitely different question than mine but have gone through them.
Find a pair of elements from an array whose sum equals a given number
https://www.w3resource.com/javascript-exercises/javascript-array-exercise-26.php
Code:
function evaluate() {
const input = prompt("Please enter the array of integers in the form: 1,2,3,1")
.split(',')
.map(item => item.trim());
function pairs(items) {
}
if (input == "" || input == null) {
document.writeln("Sorry, there is nothing that can be calculated.");
} else {
document.writeln("Your calculation is: ");
document.writeln(pairs(input) + " with a starting input string of: " + input);
}
}
evaluate()
You could iterate the array and a copy of the array beginning by the actual index plus one for getting the products. Store the result in an object with product as key.
Then get the keys (products) of the object, filter it to get only the results with two or more products.
var array = [1, 2, 3, 7, 8, 9, 6],
result = {},
pairs;
array.forEach(function (a, i) {
array.slice(i + 1).forEach(function (b) {
(result[a * b] = (result[a * b] || [])).push([a, b]);
});
});
pairs = Object
.keys(result)
.filter(function (k) { return result[k].length >= 2; })
.map(function(k) { return result[k]; });
console.log(pairs);
We could mutate the equation:
a * b = c * d | : b
a = c * d : b
So actually we just need to get all different combinations of three numbers (b, c, d) and check if the result (a) is also in the given range:
while(true){
// shuffle
const [b, c, d] = items;
const a = c * d / b;
if(items.includes(a + ""))
return true;
}
return false;
Now you only need to shuffle the array to go through all different combinations. You can find an algorithm here
Assuming that you are given an array such as [1,2,3,7,8,9,6] and a value 18 and you need to find pairs that multiply to 18 then, use the following approach
Convert them to a map - O(n)
var inputArr = [1,2,3,7,8,9,6];
var map = inputArr.reduce( (acc, c) => {
acc[ c ] = true; //set any truthy value
return acc;
},{});
Iterate an inputArr and see if its compliment is available in the map - O(n)
var output = [];
var mulValue = 18;
inputArr.forEach( s => {
var remainder = mulValue/s;
if ( map[s] && map[remainder] )
{
output.push( [ s, remainder ] );
map[s] = false;
map[remainder] = false;
}
});
Demo
var inputArr = [1, 2, 3, 7, 8, 9, 6];
var map = inputArr.reduce((acc, c) => {
acc[c] = true; //set any truthy value
return acc;
}, {});
var output = [];
var mulValue = 18;
inputArr.forEach(s => {
var remainder = mulValue / s;
if (map[s] && map[remainder]) {
output.push([s, remainder]);
map[s] = false;
map[remainder] = false;
}
});
console.log(output);
You can try something like this:
Idea:
Loop over the array to compute product. Use this iterator(say i) as get first operand(say op1).
Now again loop over same array but the range will start from i+1. This is to reduce number of iteration.
Now create a temp variable that will hold product and operand.
On every iteration, add value to product in hashMap.
Now loop over hashMap and remove any value that has length that is less than 2.
function sameProductValues(arr) {
var hashMap = {};
for (var i = 0; i < arr.length - 1; i++) {
for (var j = i + 1; j < arr.length; j++) {
var product = arr[i] * arr[j];
hashMap[product] = hashMap[product] || [];
hashMap[product].push([arr[i], arr[j]]);
}
}
for(var key in hashMap) {
if( hashMap[key].length < 2 ) {
delete hashMap[key];
}
}
console.log(hashMap)
}
sameProductValues([1, 2, 3, 7, 8, 9, 6])

Javascript unlimited nested array handling

I am trying to have fun with my buddy who solved the problem mentioned in 8m 7s, and for me it is already 20m gone. I can't figure out how to handle unlimited nested array in javascript.
The problem is this:
// i will be an array, containing integers, strings and/or arrays like itself.
// Sum all the integers you find, anywhere in the nest of arrays.
So
arraySum([[1,2,false],'4','5']) will return 3 (passed)
arraySum([[1,2,3],4,5]) will return 15 (passed)
arraySum([[[[[[[[[1]]]]]]]], 1]) will return 2 (failed)
The code I wrote is:
function arraySum(i) {
sum = 0;
tmp =0;
for (var a=0; a<i.length; a++){
if (i[a] instanceof Array) {
ar = i[a];
for (var j=0; j<ar.length; j++){
tmp +=ar[j];
}
}
if (typeof i[a] == "number")
sum += i[a];
console.log(sum);
}
return sum + tmp;
}
As you can see it does not handle the last situation that I failed as I can't figure out how to handle unlimited nest in JS.
Any idea will be much appreciated.
Also try to finish it before 8m 7s, which my buddy finished in.
Inside of the if (i[a] instanceof Array) { part, you'll have to use recursion to operate on nested arrays with the same arraySum function, not just use another loop. Try this:
var arraySum = (function () {
"use strict";
var sumFunc, isArray;
sumFunc = function (arr) {
var sum, i, j, cur, toAdd;
sum = 0;
for (i = 0, j = arr.length; i < j; i++) {
cur = arr[i];
toAdd = 0;
if (isArray(cur)) {
toAdd = sumFunc(cur);
} else if (typeof cur === "number") {
toAdd = cur;
}
sum += toAdd;
}
return sum;
};
isArray = Array.isArray || function (obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
};
return sumFunc;
}());
DEMO: http://jsfiddle.net/Q7JPM/1
The function just loops through all items in an array, and returns the sum of any numbers found inside. If the item is an array itself, it calls arraySum and passes that array...adding the result to the sum. If it's a number, it simply adds that to the sum.
You have to use recursion:
http://jsfiddle.net/HMnat/2
function arraySumRec(theArray)
{
var sum=0;
for (var i=0;i<theArray.length;i++)
{
if (theArray[i] instanceof Array)
{
sum=sum+arraySumRec(theArray[i]);
}
else
{
if (typeof(theArray[i])=="number")
{
sum=sum+theArray[i];
}
}
}
return sum;
}
Took me 3 minutes 47 seconds (due to a typo, ha ha).
The Javascript Array reduce method is perfect for solving this kind of problem. The reduce method takes a function with at least two arguments: the accumulator and the current element of the array. In the body of the function, you specify how each element should affect the accumulator. The second argument to the function is the starting value of the accumulator.
function sum(x) {
return x.reduce(function(accumulator, currentValue) {
if (typeof currentValue === "number") {
return accumulator + currentValue;
} else if (currentValue instanceof Array) {
return accumulator + sum(currentValue);
} else {
return accumulator;
}
}, 0);
}
JSFIDDLE
The function sum takes an array, and the reduce method reduces it to a single value. In the "else if" branch, where we find a nested array, we can simply call sum on it, get back a single value, and add that to our accumulator. In the "else" branch, we haven't found the kinds of values we're interested in so we leave the accumulator unchanged.
The documentation at MDN provides a good explanation of Array reduce with examples.
function arraySum(i) {
var l = i.length, sum = 0;
while (l--) {
if (typeof i[l] !== 'number' && !(i[l] instanceof Array)) continue;
if (i[l] instanceof Array) { sum += arraySum(i[l]); continue; }
sum += i[l];
}
return sum;
}
Non-Recursive using a stack.
function arraySum(arr)
{
var sum = 0;
while(arr.length != 0)
{
var value = arr.pop();
if(value instanceof Array)
{
for (i= 0; i< value.length; ++i)
arr.push(value[i]);
}
else if(typeof value === "number")
sum += value;
}
return sum;
}
var arr = [1, 2, [3, 4, [[[5]]]]];
console.log(arraySum(arr));
If we focus on the right parts, we can save ourselves the tedium from focusing on the wrong parts -
function arraySum (t)
{ switch (t?.constructor)
{ case Array:
return t.reduce((r, v) => r + arraySum(v), 0)
case Number:
return t
default:
return 0
}
}
console.log(arraySum([[1,2,false],'4','5']))
console.log(arraySum([[1,2,3],4,5]))
console.log(arraySum([[[[[[[[[1]]]]]]]], 1]))
3
15
2
If ?. is not yet supported in your environment, you can swap it out -
switch (t?.constructor)
switch (t && t.constructor) // <- where ?. is unsupported
Edit: it took me 2,769 days to answer the question but only a few minutes to write it :D

Categories