Iterate through array to find values that equal a specified sum - javascript

I am trying to write a function that will yield the following output:
// Required sum
console.log([3, 4, 2, 1, 2, 3, 6, 0].elementsTheSumTo(6)) // [ [3, 3], [4, 2], [6, 0] ]
console.log([7, 0, 5, 8, 0, 0, 7, 7].elementsThatSumTo(7)) // [ [7, 0], [0, 7], [0, 7], [0, 7] ]
I've tried with,
Array.prototype.elementsThatSumTo = n => {
let result = [];
for (let i = 0; i < this.length; i++) {
for (let j = 0; j < this.length; j++) {
if (j !== i && (this[i] + this[j] === n) {
result.push([ this[i], this[j] ]);
}
}
}
return result;
}
But that's yielding unexpected behavior. I was also thinking of using reduce, but that didn't seem to work either. Not sure how to figure this one out.

You need to remove the elements from the array when matches are found, which can be done with splice. You also need to use a full-fledged function to access the this, the array instance:
Array.prototype.elementsThatSumTo = function(n) {
const arr = this.slice(); // avoid mutating the input array
const result = [];
while (arr.length) {
const num1 = arr.shift();
const num2Index = arr.findIndex(num => num1 + num === n);
if (num2Index === -1) {
continue;
}
result.push([num1, arr[num2Index]]);
arr.splice(num2Index, 1);
}
return result;
}
console.log([3, 4, 2, 1, 2, 3, 6, 0].elementsThatSumTo(6)) // [ [3, 2], [4, 2], [6, 0] ]
console.log([7, 0, 5, 8, 0, 0, 7, 7].elementsThatSumTo(7)) // [ [7, 0], [0, 7], [0, 7], [0, 7] ]
Keep in mind that mutating built-in prototypes is very bad practice. If possible, consider using a standalone function instead:
const elementsThatSumTo = (arrInit, n) => {
const arr = arrInit.slice(); // avoid mutating the input array
const result = [];
while (arr.length) {
const num1 = arr.shift();
const num2Index = arr.findIndex(num => num1 + num === n);
if (num2Index === -1) {
continue;
}
result.push([num1, arr[num2Index]]);
arr.splice(num2Index, 1);
}
return result;
}
console.log(elementsThatSumTo([3, 4, 2, 1, 2, 3, 6, 0], 6)) // [ [3, 2], [4, 2], [6, 0] ]
console.log(elementsThatSumTo([7, 0, 5, 8, 0, 0, 7, 7], 7)) // [ [7, 0], [0, 7], [0, 7], [0, 7] ]

You could take a Map and store the visited elements and cound the occurence.
Array.prototype.elementsThatSumTo = function (sum) {
var map = new Map,
i, l, v
result = [];
for (i = 0, l = this.length; i < l; i++) {
v = this[i];
if (map.get(v)) {
map.set(v, map.get(v) - 1);
result.push([sum - v, v]);
continue
}
map.set(sum - v, (map.get(sum - v) || 0) + 1);
}
return result;
}
console.log([3, 4, 2, 1, 2, 3, 6, 0].elementsThatSumTo(6)) // [ [3, 2], [4, 2], [6, 0] ]
console.log([7, 0, 5, 8, 0, 0, 7, 7].elementsThatSumTo(7)) // [ [7, 0], [0, 7], [0, 7], [0, 7] ]

Array.prototype.elementsThatSumTo = function(n) {
var result = [],
len = this.length;
for (var i = 0; i < len - 1; i++)
for (var j = i + 1; j < len; j++)
if (this[i] + this[j] == n)
result.push([this[i], this[j]]);
return result;
}
console.log([3, 4, 2, 1, 2, 3, 6, 0].elementsThatSumTo(6))

That's because for every number you are also seeing for the combinations that are already covered. Just change (j=0) to (j=(i+1)) in your code it will work fine and you can also ignore the check(j==i) then.
Array.prototype.elementsThatSumTo = function(n) {
let result = [];
for (let i = 0; i < this.length; i++) {
for (let j = (i+1); j < this.length; j++) {
if (this[i] + this[j] === n) {
result.push([ this[i], this[j] ]);
}
}
}
return result;
}
console.log([3, 4, 2, 1, 2, 3, 6, 0].elementsThatSumTo(6)) // [ [3, 3], [4, 2], [6, 0] ]
console.log([7, 0, 5, 8, 0, 0, 7, 7].elementsThatSumTo(7)) // [ [7, 0], [0, 7], [0, 7], [0, 7] ]

Related

Loops & Control Flow

When I run this I can't seem to get the rest of the values.
Write a function mergingTripletsAndQuints which takes in two arrays as arguments. This function will return a new array replacing the elements in array1 if they are divisible by 3 or 5. The number should be replaced with the sum of itself added to the element at the corresponding index in array2.
function mergingTripletsAndQuints(array1, array2) {
let result = [];
let ctr = 0;
let x = 0;
for (let i = 0; i < array1.length; i++) {
for (let j = 0; j < array2.length; j++) {
ctr = array1[i] + array2[j];
if (ctr % 3 === 0 || ctr % 5 === 0) {
result.push(ctr);
} else {
return array1[i];
}
}
}
return result;
}
console.log(mergingTripletsAndQuints([1, 2, 3, 4, 5, 15], [1, 3, 6, 7, 8, 9])); // expected log [1, 2, 9, 4, 13, 24]
console.log(mergingTripletsAndQuints([1, 1, 3, 9, 5, 15], [1, 2, 3, 4, 5, 6])); // expected log [1, 1, 6, 13, 10, 21]
It is only logging [1], [1]
I'm not sure, but I suppose there is a typo returning array1[i] in nested loop. I suppose you mean result.push(array1[i]) instead.
I think it should be something like this:
function mergingTripletsAndQuints(array1, array2) {
let result = [];
for (let i = 0; i < array1.length; i++) {
if (array1[i]% 3 === 0 || array1[i]% 5 === 0) {
result.push(array1[i] + array2[i]);
} else {
result.push(array1[i]);
}
}
return result;
}
console.log(mergingTripletsAndQuints([1, 2, 3, 4, 5, 15], [1, 3, 6, 7, 8, 9])); // expected log [1, 2, 9, 4, 13, 24]
console.log(mergingTripletsAndQuints([1, 1, 3, 9, 5, 15], [1, 2, 3, 4, 5, 6])); // expected log [1, 1, 6, 13, 10, 21]
A nested for loop is not necessary, look at this code:
function mergingTripletsAndQuints(array1, array2) {
let sum = [];
for (let i = 0; Math.max(i < array1.length, i < array2.length); i++) {
if (array1[i] % 3 == 0 || array1[i] % 5 == 0) {
sum.push(array1[i] + array2[i])
} else {
sum.push(array1[i])
}
}
return sum;
}

Why JS is passing a number into the array as a string?

I'm trying to obtain // [2,6,0,8,4] from the function:
let getValidPassword = arr => {
let x = [];
for (let i in arr) {
for (let j in arr[i]) {
if (arr[i][j] % 2 !== 0) {
break;
} else {
x += arr[i][j];
}
}
}
return x
};
var loggedPasscodes = [
[1, 4, 4, 1],
[1, 2, 3, 1],
[2, 6, 0, 8],
[5, 5, 5, 5],
[4, 3, 4, 3]
];
console.log(getValidPassword(loggedPasscodes));
However when I run the typeof x, I'm getting a string(I though it was a number) and when I print x I get 26084 instead of [26084]
what witchcraft is this?
I though setting x to [ ] would make the trick...
thank you.
The problem here is that you have declared x=[] but you are modifying it as x += arr[i][j]; as soon as javascript gets to this line. It treats the array as string calling x.toString() internally and appending to that string. For example if you declare an array as a=[] and call a+=1 then a will become "1". In javascript everything is value typed, it doesn't matter what you declare when you assign some value to it or do some operation on the variable, it gets converted to the type of value.
I would recommend you to go through this
let getValidPassword = arr => {
let x = [];
let temp = [];
for (let i in arr) {
for (let j in arr[i]) {
if (arr[i][j] % 2 !== 0) {
break;
} else {
temp.push(arr[i][j]);
}
if(temp.length == arr[i].length)
x = temp.slice();
}
}
return x
};
var loggedPasscodes = [
[1, 4, 4, 1],
[1, 2, 3, 1],
[2, 6, 0, 8],
[5, 5, 5, 5],
[4, 3, 4, 3]
];
console.log(getValidPassword(loggedPasscodes));
The problem is that you are incrementing your variable, to add an element to an array you need to use the push() method.
Correct code:
let getValidPassword = arr => {
let x = [];
for (let i in arr) {
for (let j in arr[i]) {
if (arr[i][j] % 2 !== 0) {
break;
} else {
x.push(arr[i][j]);
}
}
}
return x
};
var loggedPasscodes = [
[1, 4, 4, 1],
[1, 2, 3, 1],
[2, 6, 0, 8],
[5, 5, 5, 5],
[4, 3, 4, 3]
];
console.log(getValidPassword(loggedPasscodes));
When you concatenate an array (which is what += is doing) it first converts the array and the value being appended to strings.
In order to add elements to the x array use x.push(arr[i][j]), this will insert them without type conversion.

Retrieve diagonal values from a 2d array

I'm trying to create a function that retrieves the diagonal values from a 2-d array:
input = [
[1, 2, 3, 4],
[5, 1, 2, 3],
[9, 5, 1, 2]
]
output = [[9], [5, 5], [1, 1, 1], [2, 2, 2], [3, 3], [4]]
I'm having trouble figuring out how to manipulate the indices in a nested loop... This is what I'm currently working with:
const diagonalValues = arr => {
let output = new Array(2*input.length);
for (let i = 0; i < output.length; i++) {
output[i] = [];
if (i < input.length) {
for (j = input.length-1; j>i-input.length; --j) {
console.log(i, j)
}
}
}
}
How can I accomplish this?
You could use get number of rows which is just number of arrays and number of columns which is number of elements in each inner array (assuming all arrays have the same number of elements), and based on that calculate the diagonal matrix.
const input = [
[1, 2, 3, 4],
[5, 1, 2, 3],
[9, 5, 1, 2]
]
const rows = input.length;
const columns = input[0].length;
const total = columns + rows - 1;
const result = [];
for (let i = rows - 1; i >= 0; i--) {
const row = input[i];
for (let j = 0; j < columns; j++) {
const el = input[i][j];
const pos = j + rows - i - 1;
if (!result[pos]) {
result[pos] = []
}
result[pos].unshift(el)
}
}
console.log(JSON.stringify(result))
You can do the same with reduceRight and forEach methods.
let input = [
[1, 2, 3, 4, 4],
[5, 1, 2, 8, 3],
[9, 5, 1, 2, 2],
[9, 5, 1, 2, 1]
]
const result = input.reduceRight((r, a, i) => {
a.forEach((e, j) => {
const pos = j + (input.length - i - 1)
if(!r[pos]) r[pos] = []
r[pos].unshift(e)
})
return r;
}, []);
console.log(JSON.stringify(result))
You can use this algorithm to retrieve the diagonal values from 2d-input array.
const input = [
[1, 2, 3, 4],
[5, 1, 2, 3],
[9, 5, 1, 2]]
let output = []
input.forEach(res => {
res.forEach(resp => {
// if length of array is equel to 1
if (output.filter(x => x == resp).length > 0) {
output.filter(x => x == resp)[0].push(resp)
//if length of array is greater than 1
} else if (output.filter(x => x[0] == resp).length > 0) {
output.filter(x => x[0] == resp)[0].push(resp)
} else {
let temp = []
temp.push(resp)
output.push(temp)
}
})
})
output.forEach(o => console.log(JSON.stringify(o)));
let input = [
[1, 2, 3, 4],
[5, 1, 2, 8],
[9, 5, 1, 2],
[9, 5, 1, 2],
[9, 5, 1, 2],
];
let out = [];
for (let i = 1 - input.length; i < input[0].length; i++) {
let o = [];
let y = Math.max(-i, 0);
let x = Math.max(i, 0);
while (x < input[0].length && y < input.length)
o.push(input[y++][x++]);
out.push(o)
}
out.forEach(o => console.log(JSON.stringify(o)));

Iterating over rows of 2-dimensional array containing arrays of different length

I have a function that picks all elements from a 2-dimensional array by its rows and returns a 1-dimensional array.
The array has a variable amount of columns and rows.
Example:
let arr = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
];
Returns:
[1, 5, 9, 2, 6, 10, 3, 7, 11, 4, 8, 12]
The function i came up with:
convertList = (list) => {
let result = [];
let listTotalEntries = R.sum(R.map(R.length)(list));
let mod = R.modulo(R.__, list.length);
let counterRow = -1;
for (let i = 0; i < listTotalEntries; i++) {
if (mod(i) === 0) {
counterRow++;
}
if (list[mod(i)][counterRow]) {
result.push(list[mod(i)][counterRow]);
console.log(list[mod(i)][counterRow]);
}
}
console.log(result);
return result;
};
Question: This function works only with square matrices - how can i make it work with a variable length of the contained arrays?
Example:
let arr = [
[1, 2],
[],
[9, 10, 11, 12]
];
Should return:
[1, 9, 2, 10, 11, 12]
Thanks for your help!
Muff
You had a ramda.js tag in here. With Ramda, it's pretty simple, since there are two functions that will help:
const convertList = compose(flatten, transpose);
convertList(arr); //=> [1, 9, 2, 10, 11, 12]
transpose flips a matrix over its main diagonal, that is, changing rows to columns and vice versa. flatten turns a list of lists into a plain list. So composeing like this essentially creates the equivalent of list => flatten(transpose(list)).
You can see this in action on the Ramda REPL.
I suggest to go step-by-step through the arrays
var arr1 = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],
arr2 = [[1, 2], [], [9, 10, 11, 12]];
function single(array) {
var r = [],
max = Math.max.apply(null, array.map(function (a) { return a.length; })),
i = 0, j,
l = array.length;
while (i < max) {
for (j = 0; j < l ; j++) {
i in array[j] && r.push(array[j][i]);
}
i++;
}
return r;
}
document.write('<pre>' + JSON.stringify(single(arr1), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(single(arr2), 0, 4) + '</pre>');
Did you try this simple one?
var singleDimensionArr = arr.reduce(function(prev,current){return prev.concat(current)});
For example
[
[1, 2],
[],
[9, 10, 11, 12]
].reduce(function(prev,current){return prev.concat(current)});
outputs [1, 2, 9, 10, 11, 12]
Edit:
Based on the inputs from OP below, since the concatenation needs to happen column wise
var max = Math.max.apply(null, arr.map(function (a) { return a.length; }));
var finalArr = []; for( var i = 0; i < max; i++)
{
for( var j = 0; j < arr.length; j++)
{
arr[j][i] ? finalArr.push(arr[j][i]) : "";
}
}
console.log(arr);
This example makes a big sparse array putting each item where it would belong if the array were square. Then it filters out null values which occur where no input item was present.
let arr = [
[1, 2],
[],
[9, 10, 11, 12]
];
var out = arr.reduce(function(o,n,i,a) {
for (var j=0;j<n.length;j++){
o[a.length * j + i] = n[j];
}
return o;
},[]).filter(function(n) {
return n !== null;
});
alert(JSON.stringify(out));

How to sum elements at the same index in array of arrays into a single array?

Let's say that I have an array of arrays, like so:
[
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
]
How do I generate a new array that sums all of the values at each position of the inner arrays in javascript? In this case, the result would be: [17, 10, 19]. I need to be able to have a solution that works regardless of the length of the inner arrays. I think that this is possible using some combination of map and for-of, or possibly reduce, but I can't quite wrap my head around it. I've searched but can't find any examples that quite match this one.
You can use Array.prototype.reduce() in combination with Array.prototype.forEach().
var array = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
],
result = array.reduce(function (r, a) {
a.forEach(function (b, i) {
r[i] = (r[i] || 0) + b;
});
return r;
}, []);
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
Update, a shorter approach by taking a map for reducing the array.
var array = [[0, 1, 3], [2, 4, 6], [5, 5, 7], [10, 0, 3]],
result = array.reduce((r, a) => a.map((b, i) => (r[i] || 0) + b), []);
console.log(result);
Using Lodash 4:
function sum_columns(data) {
return _.map(_.unzip(data), _.sum);
}
var result = sum_columns([
[1, 2],
[4, 8, 16],
[32]
]);
console.log(JSON.stringify(result));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>
For older Lodash versions and some remarks
Lodash 4 has changed the way _.unzipWith works, now the iteratee gets all the values passed as spread arguments at once, so we cant use the reducer style _.add anymore. With Lodash 3 the following example works just fine:
function sum_columns(data) {
return _.unzipWith(data, _.add);
}
var result = sum_columns([
[1, 2],
[4, 8, 16],
[32],
]);
console.log(JSON.stringify(result));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
_.unzipWith will insert undefineds where the row is shorter than the others, and _.sum treats undefined values as 0. (as of Lodash 3)
If your input data can contain undefined and null items, and you want to treat those as 0, you can use this:
function sum_columns_safe(data) {
return _.map(_.unzip(data), _.sum);
}
function sum_columns(data) {
return _.unzipWith(data, _.add);
}
console.log(sum_columns_safe([[undefined]])); // [0]
console.log(sum_columns([[undefined]])); // [undefined]
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
This snipet works with Lodash 3, unfortunately I didn't find a nice way of treating undefined as 0 in Lodash 4, as now sum is changed so _.sum([undefined]) === undefined
One-liner in ES6, with map and reduce
var a = [ [0, 1, 3], [2, 4, 6], [5, 5, 7], [10, 0, 3] ];
var sum = a[0].map((_, i) => a.reduce((p, _, j) => p + a[j][i], 0));
document.write(sum);
Assuming that the nested arrays will always have the same lengths, concat and reduce can be used.
function totalIt (arr) {
var lng = arr[0].length;
return [].concat.apply([],arr) //flatten the array
.reduce( function(arr, val, ind){ //loop over and create a new array
var i = ind%lng; //get the column
arr[i] = (arr[i] || 0) + val; //update total for column
return arr; //return the updated array
}, []); //the new array used by reduce
}
var arr = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
];
console.log(totalIt(arr)); //[17, 10, 19]
Assuming array is static as op showned.
a = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
]
b = []
for(i = 0; i < a[0].length; i++){
count = 0
for(j = 0; j < a.length; j++){
count += a[j][i]
}
b.push(count)
}
console.log(b)
So far, no answer using the for ... of mentioned in the question.
I've used a conditional statement for different lengths of inner arrays.
var a = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
];
i = 0;
r = []
for (const inner of a) {
j = 0;
for (const num of inner) {
if (j == r.length) r.push(num)
else r[j] += num
j++;
}
i++;
}
console.log(r);
True, in this case, the classic for cycle fits better than for ... of.
The following snippet uses a conditional (ternary) operator.
var a = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
];
r = [];
for (i = 0; i < a.length; i++) {
for (j = 0; j < a[i].length; j++) {
j==r.length ? r.push(a[i][j]) : r[j]+=a[i][j]
}
}
console.log(r);
A solution using maps and reductions, adding elements from different lengths of arrays.
var array = [
[0],
[2, 4],
[5, 5, 7, 10, 20, 30],
[10, 0]
];
b = Array(array.reduce((a, b) => Math.max(a, b.length), 0)).fill(0);
result = array.reduce((r, a) => b.map((_, i) => (a[i] || 0) + (r[i] || 0)), []);
console.log(result);
const ar = [
[0, 1, 3],
[2, 4, 6],
[5, 5, 7],
[10, 0, 3]
]
ar.map( item => item.reduce( (memo, value)=> memo+= value, 0 ) )
//result-> [4, 12, 17, 13]

Categories

Resources