I have to find the maximum path sum starting from top right to bottom left of the matrix.
Therefore this is what I was asked to do:
Given an mxn grid filled with numbers, find a path from top left to
bottom right, which maximizes the sum of all numbers along its path,
also i can only move either down or right.
So I had this on a coding interview recently and I passed it, but in the middle of the interview the guy asked me to also show the path along with the result
Example:
grid = [[1,10]
,[2,3]]
output: 14, right,down
So I came up with a solution:
function matrix(grid) {
var m = grid.length;
var n = grid[0].length;
if(!m || !n) return grid;
for (let i = 0; i < m; i++){
for (let j = 0; j < n; j++){
// grid[0][0] itself contains the min path sum
if (i === 0 && j === 0) continue;
// first row: grid[i][j] = previous grid(left) + current value
if (i === 0) grid[i][j] += grid[i][j-1];
// first column: grid[i][j] = previous grid(top) + current value
else if (j === 0) grid[i][j] += grid[i-1][j];
// grid[i][j] = get the min of previous grid(top/left) + current value
else grid[i][j] += Math.max(grid[i][j - 1], grid[i - 1][j]);
}
};
return grid[n - 1][m - 1];
};
const grid = [[1,10],[2,3]];
console.log(matrix(grid));
What I tried during the interview was to add the given grid and the result of the maximum numbers on each step on a variable each and then try to subtract the numbers that was calculated in the la line on my 2nd for loop compare it with the number in the original grid and if it match then push 'down' or 'right' to an empty array and then simply return the result and the array.
Example:
grid = [[1,10]
,[2,3]]
oldGrid = grid
arr == []
loop
on each iteration
grid[1] - oldGrid[1] = oldgrid[0]
{for rows}
add "right" to arr
or
grid[2] - oldgrid[2] = oldgrid[0]
{for cols}
add "down" to arr
return result and arr
so this is what i've come up with
function matrix(grid) {
var m = grid.length;
var n = grid[0].length;
var showDir = [];
if(!m || !n) return grid;
for (let i = 0; i < m; i++){
for (let j = 0; j < n; j++){
// grid[0][0] itself contains the min path sum
if (i === 0 && j === 0) continue;
// first row: grid[i][j] = previous grid(left) + current value
if (i === 0) grid[i][j] += grid[i][j-1];
// first column: grid[i][j] = previous grid(top) + current value
else if (j === 0) grid[i][j] += grid[i-1][j];
// grid[i][j] = get the min of previous grid(top/left) + current value
else{
let direction, max;
if(grid[i][j-1]>grid[i-1][j]){
direction = "R"
max = grid[i][j-1]
showDir.push(direction)
}
else{
direction = "D"
max = grid[i-1][j]
showDir.push(direction)
}
grid[i][j] += max;
}
};
};
return grid[n - 1][m - 1], showDir
};
const grid = [[1,10],[2,3]];
console.log(matrix(grid));
the thing is with this it only output showDir not the grid and i don't
know why
I'm new to Javascript so please go easy on me
You could iterate the diagonals and take the maximum value of either the upper item or the right one.
The final result is in bottom left item.
To get a path, you start from the end and choose the item with the next larger sum and take this position for the result set and get the next larger item's position until you reach the start item.
const
exist = (i, j) => i >= 0 && i < size[0] && j >= 0 && j < size[1],
array = [[1, 3, 4, 2, 5], [2, 4, 1, 7, 6], [9, 4, 1, 2, 1], [3, 5, 1, 1, 2], [3, 3, 7, 1, 4]],
size = [array.length, array[0].length],
sums = [],
path = [];
// get sums
for (let k = 0, l = size[0] + size[1] - 1; k < l; k++) {
let i = Math.min(k, size[0] - 1),
j = k - i;
while (exist(i, j)) {
sums[i] ??= [];
sums[i][j] = array[i][j] + Math.max(sums[i - 1]?.[j] || 0, sums[i][j - 1] || 0);
i--; j++;
}
}
// get path from max to start value
let i = size[0] - 1,
j = size[1] - 1;
path.push([i, j]);
while (i || j) {
if (!exist(i - 1, j) || exist(i, j - 1) && sums[i - 1][j] < sums[i][j - 1]) j--;
else i--;
path.unshift([i, j]);
}
path.forEach(([i, j]) => console.log(i, j, '->', sums[i][j]));
console.log('');
sums.forEach(a => console.log(...a.map(v => v.toString().padStart(3, ' '))));
.as-console-wrapper { max-height: 100% !important; top: 0; }
My approach is to build up a new grid with values such as {value: 56, path:["right", "down", "right", "down"]}, by iterating over the rows and columns and calculating each according to the one above it and the one to the left of it, if they exist, turning this:
[
[1, 10, 7],
[2, 30, 9],
[25, 2, 6]
]
into something like this:
[
[{value: 1, path: []}, {value: 11, path: [➡]}, {value: 18, path: [➡➡]}]
[{value: 3, path: [⬇]}, {value: 41, path: [➡⬇]}, {value: 50, path: [➡⬇➡]}]
[{value: 28, path: [⬇⬇]}, {value: 43, path: [➡⬇⬇]}, {value: 56, path: [➡⬇➡⬇]}]
]
(with the strings "right" and "down" replaced by arrows for simpler display) and then simply reading off the bottom-right value.
Here is some code doing that with a double-reduce to iterate the rows and then the cells within those rows:
const maxPath = (grid) => grid .reduce ((rs, r, j) => [...rs, r .reduce ((cs, c, i, _,
above = j > 0 && rs [j - 1] [i],
left = i > 0 && cs [i - 1]
) => [
... cs,
above && (!left || above .value > left .value)
? {value: above .value + c, path: [... above .path, 'down']}
: left && (!above || left .value >= above .value)
? {value: left .value + c, path: [... left .path, 'right']}
: {value: c, path: []}
], [])], []) [grid .length - 1] [grid [0] .length - 1]
console .log (maxPath ([
[1, 10, 7],
[2, 30, 9],
[25, 2, 6]
]))
The conditions in that version are a little complex. I started with this version:
const maxPath = (grid) => grid .reduce ((rs, r, j) => [...rs, r .reduce ((cs, c, i, _,
above = j > 0 && rs [j - 1] [i],
left = i > 0 && cs [i - 1]
) => [
... cs,
above && left
? above .value > left .value
? {value: above .value + c, path: [... above .path, 'down']}
: {value: left .value + c, path: [... left .path, 'right']}
: above
? {value: above .value + c, path: [... above .path, 'down']}
: left
? {value: left .value + c, path: [... left .path, 'right']}
: {value: c, path: []}
], [])], []) [grid .length - 1] [grid [0] .length - 1]
and then combined those conditions which led to the same result.
Related
I wrote a program to:
Print the new array of elements
Print the sum of all elements (or integers)
Actually, I got it right, however, the little problem is, I want to maintain all the duplicates (still within the range of four largest elements). Here's what I mean:
Take an array of numbers: [4,5,-2,3,1,2,6,6]
The four largest numbers are 4,5,6,6. And their sum is 4+5+6+6=21
What the code is doing (not good):
Instead of getting "6,6,5,4" as (described above), the code is printing "6,5,4,3" with the sum as 18.
ALSO, when there are only four elements [with or without duplicates] as in [1,1,1,-5],
let it just add ALL elements. You guessed it, the sum of all elements is -2
How do I order the program to print the necessary duplicate(s) to make the four largest integers?
Here's my code...
var arr = new Array(4,5,-2,3,1,2,6,6);
// var arr = new Array(1,1,1,-5);
// var largArr = new Array();
function largest() {
largArr = Array(0, 0, 0, 0)
for (i = 0; i < arr.length; i++) {
if (arr[i] > largArr[0]) {
largArr[0] = arr[i];
}
}
for (i = 0; i < arr.length; i++) {
if (arr[i] > largArr[1] && arr[i] < largArr[0]) {
largArr[1] = arr[i];
}
}
for (i = 0; i < arr.length; i++) {
if (arr[i] > largArr[2] && arr[i] < largArr[1]) {
largArr[2] = arr[i];
}
}
for (i = 0; i < arr.length; i++) {
if (arr[i] > largArr[3] && arr[i] < largArr[2]) {
largArr[3] = arr[i];
}
}
console.log(largArr[0], largArr[1], largArr[2], largArr[3]);
console.log(largArr[0] + largArr[1] + largArr[2] + largArr[3]);
}
largest();
I believe there is a genius out there who can help me solve this :)
You could get the top four and filter the original array.
function get4Largest(array) {
const top4 = [...array].sort((a, b) => b - a).slice(0, 4);
return array.filter(v => {
if (top4.includes(v)) {
top4.splice(top4.indexOf(v), 1);
return true;
}
});
}
console.log(get4Largest([4, 5, -2, 3, 1, 2, 6, 6]));
A different approach by keeping indices.
function get4Largest(array) {
return array
.map((v, i) => [v, i])
.sort(([a], [b]) => b - a)
.slice(0, 4)
.sort(([, a], [, b]) => a - b)
.map(([v]) => v);
}
console.log(get4Largest([4, 5, -2, 3, 1, 2, 6, 6]));
If you want sum of largest four numbers then you can easily do with sort, slice and reduce:
numbers.sort((a, b) => b - a).slice(0, 4).reduce((acc, curr) => acc + curr, 0)
const numbers = [4, 5, -2, 3, 1, 2, 6, 6];
const result = numbers
.sort((a, b) => b - a)
.slice(0, 4)
.reduce((acc, curr) => acc + curr, 0);
console.log(result);
You can use a reduce, sort and slice methods of an array like so:
function sumMaxValues (maxLength, values) {
return values
.sort((v1, v2) => v1 > v2 ? -1 : 1)
.slice(0, maxLength)
.reduce((sum, v) => sum + v, 0)
}
console.log(
sumMaxValues(4, [4, 5, -2, 3, 1, 2, 6, 6, 10]),
)
Edit: I fixed, a bug that #gog pointed out. The root cause of a problem was that sort when invoked without a compareFn then "the array elements are converted to strings, then sorted according to each character's Unicode code point value."(sort docs)
If for some reason you want to have a classical type of solution, that avoids modern javascript methods, here's one
const arr = Array(4, 5, -2, 3, 1, 2, 6, 6);
//const arr = Array(1, 1, 1, -5);
function largest(){
const largArr = Array(-1/0, -1/0, -1/0, -1/0);
for(let i = 0; i < arr.length; i++){
for(let j = 0; j < largArr.length; j++){
if(arr[i] > largArr[j]){
for(let k = largArr.length - 2; k >= j; k--){
largArr[k + 1] = largArr[k];
}
largArr[j] = arr[i];
break;
}
}
}
let sum = 0;
for(let j = 0; j < largArr.length; j++){
if(largArr[j] === -1/0){
largArr[j] = 0;
}
sum += largArr[j];
}
console.log(largArr, sum);
}
largest();
-1/0 stands for minus infinity (there can be no smaller number); you may also use Number.NEGATIVE_INFINITY for it. If it's too exotic for your needs, replace -1/0 with any number you are certain is less than any possible number in the array (that however, cannot be zero, since you allow negative numbers also).
I want to write a function with for-loops that finds the index of the number 1 in an array and returns the difference to the index of the number 2 that is closest to number 1 (number 1 only appears once). For instance:
Input: [1, 0, 0, 0, 2, 2, 2]
Output: 4
Input: [2, 0, 0, 0, 2, 2, 1, 0, 0 ,2]
Output: 1
My try
function closest (array) {
let elem=array.findIndex(index=>index === 1)
let numberplus=0;
let numberminus=0;
for (let i=elem; i<array.length; i++){
if (array[elem+1] === 2)
{numberplus+=array[elem+1]-elem;}
break;
}
for (let i=elem; i>=0; i--) {
if (array[elem-1] ===2)
{numberminus+=array[elem-1]-elem;}
break;
}
if (numberplus < numberminus) {
return numberplus
} else {
return numberminus}
}
When invoked, the function just returns '0'. Thanks for reading!
Take the position of 1 as starting point and loop up and (if necessary) down the array:
const log = (arr, d) => console.log(`mimimal distance [${arr.join()}]: ${d}`);
const arr = [2, 0, 0, 0, 2, 2, 1, 0, 0, 2];
const arr2 = [1, 0, 0, 0, 2, 2, 2];
const arr3 = [2, 0, 1, 0, 2, 2, 2];
const arr4 = [2, 1, 0, 0, 2, 2, 2];
log(arr, clostes(arr));
log(arr2, clostes(arr2));
log(arr3, clostes(arr3));
log(arr4, clostes(arr4));
function clostes(arr) {
// determine position of 1
const indxOf1 = arr.indexOf(1);
// create array of distances
const distances = [0, 0];
// forward search
for (let i = indxOf1; i < arr.length; i += 1) {
if (arr[i] === 2) {
break;
}
distances[0] += arr[i] !== 2 ? 1 : 0;
}
// if 1 is # position 0 backwards search
// is not necessary and minimum equals the
// already found maximum
if (indxOf1 < 1) {
distances[1] = distances[0];
return Math.min.apply(null, distances);
}
// backwards search
for (let i = indxOf1; i >= 0; i -= 1) {
if (arr[i] === 2) {
break;
}
distances[1] += arr[i] !== 2 ? 1 : 0;
}
return Math.min.apply(null, distances);
}
Something like this will do the job. You could make the code shorter but I've tried to make it clear. Once we find 1, start at that index and keep checking adjacent indices. We also do bounds checking to ensure we don't overflow either end.
function closest(arr) {
const index = arr.findIndex(n => n === 1);
const len = arr.length;
let offset = 1;
while (true) {
const before = index - offset;
const after = index + offset;
const beforeBad = before < 0;
const afterBad = after >= len;
// It's necessary to check both, we could exceed the bounds on one side but not the other.
if (beforeBad && afterBad) {
break;
}
if ((!beforeBad && arr[before] === 2) || (!afterBad && arr[after] === 2)) {
return offset;
}
++offset;
}
return -1;
}
You could approach this using entries and reduce.
const arr = [2, 0, 0, 0, 2, 2, 1, 0, 0 ,2];
const goal = arr.indexOf(1);
const indices = [];
// Find all the indices of 2 in the array
for (let x of arr.entries()) {
if (x[1] === 2) indices.push(x[0]) ;
}
// Find the index that is closest to your goal
const nearestIndex = indices.reduce((prev, curr) => {
return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
}); // 5
console.log(Math.abs(goal - nearestIndex)); // 1
How about this:
Output = Input.map((cur,idx,arr)=>cur==2?Math.abs(idx-arr.indexOf(1)):Infinity).sort()[0]
You could avoid for loops here in favor of a more functional style. The function minDist takes m, n, and array as arguments, and returns the minimum distance between the first occurrence of m and any occurrence of n in an array.
First, map is used to create an array with pairs for each element containing the distance to the target m element and the value of the current element. Then filter is used to keep only the pairs representing n elements. Then sort is used so that the pairs representing the closest elements are at the beginning of the array. Finally, the [0] pair of the sorted array represents the closest element, and the [0] element of this closest pair is the minimum distance.
function minDist(m, n, array) {
let index = array.indexOf(m);
return array
.map((x, i) => [Math.abs(i - index), x])
.filter(p => p[1] === n)
.sort()[0][0];
}
console.log(minDist(1, 2, [1, 0, 0, 0, 2, 2, 2]));
console.log(minDist(1, 2, [2, 0, 0, 0, 2, 2, 1, 0, 0, 2]));
Let's say I have an array [0, null, null, 3, null, null, null, 11].
I want to fill null values with numbers based on previous and next known number (and index?), so I get [0, 1, 2, 3, 5, 7, 9, 11]. What is the most efficient way to do so?
I'm thinking about something that could count nulls between two known numbers and then just get the size of one step. But these steps will be different between pairs
I'm working on a chart where some values may be missing so I have to fill possible values.
This is what I've tried, but I think it's extremely inefficient and messy. I would prefer to use ramda.js or some functional approach.
const data = [0, null, null, 3, null, null, null, 11]
const getStep = (arr, lastKnown = 0, counter = 1) => {
const val = arr[0];
if (val !== null) {
return (val - lastKnown) / counter
} else {
return getStep(arr.slice(1), lastKnown, ++counter)
}
}
let lastKnown = null
let currentStep = null
const filledData = data.map((x, i) => {
if (x !== null) {
lastKnown = x
currentStep = null
return x
}
if (currentStep !== null) {
lastKnown = lastKnown + currentStep
} else {
currentStep = getStep(data.slice(i), lastKnown)
}
return currentStep + lastKnown
})
console.log(filledData)
// UPDATE: I've selected THIS ANSWER as correct, but If you're interested in a solution you should definitely check all answers here. There are some very interesting ideas.
You could iterate the array and if a null value is found, a look ahead is made for the next numbers and the gaps until the number are filled by taking a linear approach.
var array = [0, null, null, 3, null, null, null, 11],
i = 0, j, delta;
while (i < array.length) {
if (array[i] !== null) {
i++;
continue;
}
j = i;
while (array[++j] === null);
delta = (array[j] - array[i - 1]) / (j - i + 1);
do {
array[i] = delta + array[i - 1];
i++;
} while (i < j)
}
console.log(array);
ES6 with a closure over the next index with a numerical value, the real last value of the predecessor and the delta for adding for the next value if not given.
var array = [0, null, null, 3, null, null, null, 11],
result = array.map(((j, last, delta) => (v, i, a) => {
if (v !== null) return last = v;
if (i < j) return last += delta;
j = i;
while (++j < a.length && a[j] === null) ;
delta = (a[j] - last) / (j - i + 1);
return last += delta;
})());
console.log(result);
One way to do this using for loops and counting:
var skips = 0;
var last;
for (var i=0; i<arr.length; i++){
var current = arr[i]
if (current !== null) {
// If there are skipped spots that need to be filled...
if (skips > 0){
// Calculate interval based on on skip count, and difference between current and last
var interval = (current-arr[last])/(skips+1);
// Fill in the missing spots in original array
for (var j=1; j<=skips; j++){
arr[last+j] = arr[last]+(interval*j)
}
}
last = i; // update last valid index
skips = 0; // reset skip count
}
// If null, just increment skip count
else {
skips++
}
}
Another approach to this is to convert your input array into a list of "segments" capturing the start value, end value and size of each segment. You can then use R.chain to build up the list with a linear step between the start and end values of each segment.
const input = [0, null, null, 3, null, null, null, 11]
// recursively convert the sparse list of numbers into a list of segments
const segmentNull = xs => {
if (xs.length === 0) {
return []
} else {
const [y, ...ys] = xs
const count = R.takeWhile(R.isNil, ys).length + 1
const next = R.dropWhile(R.isNil, ys)
return next.length > 0
? R.prepend({ start: y, end: next[0], count }, segmentNull(next))
: []
}
}
// segmentNull(input)
//=> [{"count": 3, "end": 3, "start": 0}, {"count": 4, "end": 11, "start": 3}]
// produce a list of `count` values linearly between `start` and `end` values
const linearRange = (start, end, count) =>
R.times(n => (end - start) * (n + 1) / count + start, count)
// linearRange(3, 11, 4)
//=> [5, 7, 9, 11]
// convert the list of segments into a list of linear values between segments
const buildListFromSegments = R.chain(({ start, end, count }) =>
linearRange(start, end, count))
// buildListFromSegments(segmentNull(input))
//=> [1, 2, 3, 5, 7, 9, 11]
// ^-- note the leading 0 is missing
// prepend the initial value to the result of `buildListFromSegments`
const fn = xs => R.prepend(xs[0], buildListFromSegments(segmentNull(xs)))
console.log(fn(input))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
An O(n*m) solution where n is the count of all elements and m is the count of nulls.
Algorithm assumes there will always be valid digita at index positions 0 and length-1.
function fillInTheBlanks(a){
var s, //step
r = a.reduce(function([t,ns,r], e){ // [temp, nulls array, result accumulator]
e === null ? ns.push(e)
: t === void 0 ? t = e
: (s = (e-t)/(ns.length+1),
r.push(t,...ns.map((_,i) => t+(i+1)*s)),
ns = [],
t = e);
return [t,ns,r];
}, [void 0,[],[]]);
return r[2].concat(r[0]);
}
var arr = [0, null, null, 3, null, null, null, 11],
res = fillInTheBlanks(arr);
console.log(JSON.stringify(res));
Here is my quick solution with ramda:
const xs = [0, null, null, 3, null, null, null, 11]
const scanWithIndex = R.addIndex(R.scan)
const notNil = R.complement(R.isNil)
const mapWithIndex = R.addIndex(R.map)
const zipArrays = R.zipWith(R.concat)
// number of cons nulls for nth element
const consNulls = R.drop(1, R.scan((acc, x) => R.isNil(x) ? (acc + 1) : 0, 0, xs))
// length of ongoing null sequence for each element
const consNullsSeqLens = R.drop(1, scanWithIndex((acc, x, ind) =>{
if (x !== 0 && acc !== 0) return acc
const rest = R.drop(ind, consNulls)
return R.findIndex(R.equals(0), rest)
}, 0, consNulls))
// previous non null value for each el
const prevNonNulls = R.scan((acc, x) => R.isNil(x) ? acc : x, 0, xs)
// next non null value for each el
const nextNonNulls = mapWithIndex((x, ind) => {
const rest = R.drop(ind, xs)
return R.find(notNil, rest)
}, xs)
// function to calculate missing values based on zipped arrays
const calculateMissingValue = ([x, seqN, seqLen, next, prev]) =>
R.isNil(x) ? prev + (next - prev) / (seqLen + 1) * seqN : x
R.map(
calculateMissingValue,
// zips 5 lists together
zipArrays(
zipWith(R.append, consNullsSeqLens, R.zip(xs, consNulls)),
R.zip(nextNonNulls,prevNonNulls)
)
)
Repl link
While the answer from #bubulik42 shows that you can use Ramda in doing this, I'm not sure Ramda is going to be much of a help. (Disclaimer: I'm one of Ramda's authors.)
My first pass at this looked like this:
const intersperseNulls = pipe(
reduce(
({vals, prev, nilCount}, curr) => isNil(curr)
? {vals: vals, prev: prev, nilCount: nilCount + 1}
: (nilCount < 1)
? {vals: append(curr, vals), prev: curr, nilCount: 0}
: {
vals: append(curr, concat(vals, times(n => prev + (n + 1) * (curr - prev) / (nilCount + 1), nilCount))),
prev: curr,
nilCount: 0
},
{vals: [], prev: undefined, nilCount: 0},
),
prop('vals')
);
This uses the usually-functional reduce call, but it is a somewhat odd use of it, choosing to pass state through all the iterations rather than a simple accumulator. Note how similar it looks if I remove the Ramda infrastructure:
const steps = (b, e, c) => {
const results = []
for (let i = 0; i < c; i++) {results.push(b + (i + 1) * (e - b) / (c + 1));}
return results;
}
const intersperseNulls = array => array.reduce(
({vals, prev, nilCount}, curr) => (curr == null)
? {vals: vals, prev: prev, nilCount: nilCount + 1}
: (nilCount < 1)
? {vals: vals.concat(curr), prev: curr, nilCount: 0}
: {
vals: vals.concat(steps(prev, curr, nilCount)).concat(curr),
prev: curr,
nilCount: 0
},
{vals: [], prev: undefined, nilCount: 0},
).vals
only times was difficult to replace.
But in the end, I prefer the non-Ramda solution from #Nina Scholz. It's simpler, more easily readable, and doesn't try any trickery.
You can see these in the Ramda REPL.
To expand a little bit the question: Fill missing numeric values in an array?.
The following will fill any zero, in the most natural way, related to others numbers in the array.
To create referential scales 📈, with natural increments.
/* Array Zero Values Natural fill
Create a referential scale, as a ruler */
const naturalFill = (array) => {
let missing = [];
let keys = [];
let increment = 0;
for (let i = 0; i <= array.length; i++) {
if (array[i] !== 0) {
keys.push(i)
}
}
for (let i = 0; i < keys.length-2; i++) {
let slots = keys[i+1] - keys[i],
min = array[keys[i]],
max = array[keys[i+1]];
increment = ((max - min) / slots);
let afill = [...Array(slots + 1)].map((x, y) => +(min + increment * y).toFixed(4)).slice(0, -1);
missing = [...missing, ...afill]
}
let upfill = [...Array(keys[0] + 1)].map((x, y) => +(array[keys[0]] - increment * y).toFixed(4)).reverse().slice(0, -1);
let downfill = [...Array(keys[keys.length - 2] + 1)].map((x, y) => +(array[keys[keys.length - 2]] + increment * y).toFixed(4));
return [...upfill, ...missing, ...downfill]
}
// Example 1
console.log(
naturalFill( [0, 0, 14, 0, 107, 0, 314, 0, 400, 0, 832, 987, 0, 0] )
)
// Example 2, generate array of epoch intervals
console.log(
naturalFill( [0,0, Date.now()-60*60*24, 0,0,0,0,0, Date.now(), 0,0,0] )
)
This might be useful in many ways, like to create graph charts referentials 📊.
Or simply to measure a previously scaled object from any key step point.
We can use it to generate sequential timestamps, as the example 2.
Here is the prompt: Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution.
Example:
Given nums = [2, 11, 15, 7], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
Here is my solution, but it doesn't seem to give the output I expected:
var sumTarget = function(array, target) {
var result = [];
var copy = array.slice();
var firstValue = array.shift();
if (array.length === 0) {
return result;
}
for (var i = copy.indexOf(firstValue) + 1; i < copy.length; i++) {
if (firstValue + copy[i] === target) {
Array.prototype.push.apply(result, [copy.indexOf(firstValue), i]);
}
}
return sumTarget(array, target);
};
Something like this:
https://jsfiddle.net/rqp93gpy/2/
function getIndexes(arr, target, offset) {
var result = [], i;
if (arr.length <= 1) return [];
if (offset === undefined) offset = 0;
for (i = 1; i < arr.length; i++) {
if (arr[0] + arr[i] === target) {
result.push([offset, offset + i]);
}
}
return result.concat(getIndexes(arr.slice(1), target, offset + 1));
}
console.log(JSON.stringify(getIndexes([2, 11, 15, 7, 6, 3, 4, 8, 9, 5, 7], 9),
null, 4));
output:
[
[
0,
3
],
[
0,
10
],
[
4,
5
],
[
6,
9
]
]
Although the other answer which is based on a recursive method seems neater it's algorithm is brute and naturally expected to be somewhat less efficient in the performance. This approach is more functional and turns out to be 1.5 times faster on repl.it, 2.5 times faster on Opera, 8 times faster on Firefox and 25 times faster on Chrome console tests.
Roughly the logic is as follows;
Filter out the items greater than the target
Separate odd and even array items by establishing two separate hash table objects (LUT) in which we keep a unique copy of the valid array items as properties and each property has an array of it's indexes.
Perform two separate algorithms depending on target being odd or even
Let's see;
var arr = [4,6,12,5,8,4,3,9,19,5,21,13,8,15,7,23,6,11,10,15,1,12,19,31,14,6,3,16],
tar = 12;
function getIndexes(arr, target){
var odds = {},
evens = {},
results = [],
makeResult = (a,b) => !!b ? a.forEach( e => b.forEach( f => results.push([e,f])))
: a.reduce((p,c,i) => {makeResult([p],a.slice(i)); return c});
arr.forEach((e,i) => e < target ? e%2 == 1 ? !!odds[e] ? odds[e].push(i) : ( odds[e] = [], odds[e].push(i))
: !!evens[e] ? evens[e].push(i) : (evens[e] = [], evens[e].push(i))
: false);
var oko = Object.keys(odds),
oke = Object.keys(evens);
target%2 == 1 ? oko.length <= oke.length ? oko.forEach( e => evens[target-e] && makeResult( odds[e], evens[target-e]))
: oke.forEach( e => odds[target-e] && makeResult(evens[e], odds[target-e]))
: (oko.forEach( e => (e <= target/2 && odds[target-e]) && (e < target/2 ? makeResult( odds[e], odds[target-e])
: makeResult( odds[e]))),
oke.forEach( e => (e <= target/2 && evens[target-e]) && (e < target/2 ? makeResult(evens[e], evens[target-e])
: makeResult(evens[e]))));
return results;
}
document.write('<pre>' + JSON.stringify(getIndexes(arr, tar), 0, 2) + '</pre>');
For those who would like recursion there is a tiny bit of recursion in the makeResults function.
As per performance comparison you may check https://repl.it/CIxd/1 vs https://repl.it/CIxr
Vote for this:
function sumTarget(ar, t) {
var res = [];
for (var i = 0, n = ar.length; i < n-1; i++) {
for (var j = i + 1; j < n; j++) {
if (ar[i] + ar[j] == t) {
res.push({ num1: i, val1: ar[i], num2: j, val2: ar[j] });
}
}
}
console.log(JSON.stringify(res));
}
sumTarget([2, 11, 15, 7], 9);
I hope it will help in your class ;)
I want to loop through an array and then add each value to each other (except itself + itself) and if the sum of the two values that were looped through equals the second argument in my function, and the pair of values hasn't been encountered before, then remember their indices and, at the end, return the full sum of all remembered indices.
In other words, the problem statement is: given an array A of integers and a second value s that is a desired sum, find all pairs of values from array A at indexes i, j such that i < j and A[i] + A[j] = s, and return the sum of all indexes of these pairs, with the following restriction:
don't reuse value pairs, i.e. if two index pairs i, j and k, l satisfying the above conditions are found and if A[i] == A[k] and A[j] == A[l] or A[i] == A[l] and A[j] == A[k], then ignore the pair with the higher index sum.
Example
For example, functionName([1, 4, 2, 3, 0, 5], 7) should return 11 because values 4, 2, 3 and 5 can be paired with each other to equal 7 and the 11 comes from adding the indices of them to get to 11 where:
4 + 3 = 7
5 + 2 = 7
4 [index: 1]
2 [index: 2]
3 [index: 3]
5 [index: 5]
1 + 2 + 3 + 5 = 11
Example #2
functionName([1, 3, 2, 4], 4) would only equal 1, because only the first two elements can be paired to equal 4, and the first element has an index of 0 and the second 1
1 + 3 = 4
1 [index: 0]
3 [index: 1]
0 + 1 = 1
This is what I have so far:
function functionName(arr, arg) {
var newArr = [];
for(var i = 0; i < arr.length; i++){
for(var j = i + 1; j < arr.length; j++) {
if((arr[i] + arr[j]) === arg ) {
newArr.push(i , j);
}
}
}
if(newArr.length === 0) {
return console.log(0);
}
return console.log(newArr.reduce(function(a,b){return a + b}));
}
functionName([1, 4, 2, 3, 0, 5], 7);
The problem I have is that it all works but I have the issue that once it finds a pair that equals the second argument, then it's not supposed to use the same value pairs again but mine does, e.g.:
if the array is [1,1,1] and the second argument is 2, the loop will go through and find the answer but it continues to search after it finds the sum and I only want it to use the pair [1, 1] once, so if it finds a pair like this at indexes [0, 1] then it should not include any other pair that contains the value 1.
I was thinking that i could remove the rest of the values that are the same if more than 2 are found using filter leaving me with only 2 of the same value if there is in an array thus not having to worry about the loop finding a 1 + 1 twice but is this the best way to go about doing it?
I'm still new to this but looking forward to your comments
PS I'm planning on doing this using pure JavaScript and no libraries
Link to a JS fiddle that might make things easier to see what I have.
https://jsfiddle.net/ToreanJoel/xmumv3qt/
This is more complicated than it initially looks. In fact, making a loop inside a loop causes the algorithm to have quadratic time complexity with regard to the size of the array. In other words, for large arrays of numbers, it will take a very long time to complete.
Another way to handle this problem is to notice that you actually have to use each unique value in the array only once (or twice, if s is even and you have two s/2 values somewhere in the array). Otherwise, you would have non-unique pairs. This works because if you need pairs of numbers x and y such that x + y = s, if you know x, then y is determined -- it must be equal s - x.
So you can actually solve the problem in linear time complexity (to be fair, it's sometimes n*log(n) if all values in A are unique, because we have to sort them once).
The steps of the algorithm are as follows:
Make a map whose keys are values in array A, and values are sorted lists of indexes these values appear at in A.
Move through all unique values in A (you collected them when you solved step 1) in ascending order. For each such value:
Assume it's the lower value of the searched pair of values.
Calculate the higher value (it's equal to s - lower)
Check if the higher value also existed in A (you're doing it in constant time thanks to the map created in step 1).
If it does, add the lowest indexes of both the lower and the higher value to the result.
Return the result.
Here's the full code:
function findSumOfUniquePairs(numbers, sum) {
// First, make a map from values to lists of indexes with this value:
var indexesByValue = {},
values = [];
numbers.forEach(function (value, index) {
var indexes = indexesByValue[value];
if (!indexes) {
indexes = indexesByValue[value] = [];
values.push(value);
}
indexes.push(index);
});
values.sort();
var result = 0;
for (var i = 0, maxI = values.length; i < maxI; ++i) {
var lowerValue = values[i],
higherValue = sum - lowerValue;
if (lowerValue > higherValue) {
// We don't have to check symmetrical situations, so let's quit early:
break;
}
var lowerValueIndexes = indexesByValue[lowerValue];
if (lowerValue === higherValue) {
if (lowerValueIndexes.length >= 2) {
result += lowerValueIndexes[0] + lowerValueIndexes[1];
}
} else {
var higherValueIndexes = indexesByValue[higherValue];
if (higherValueIndexes) {
result += lowerValueIndexes[0] + higherValueIndexes[0];
}
}
}
return result;
}
document.write(findSumOfUniquePairs([1, 4, 2, 3, 0, 5], 7) + '<br>'); // 11;
document.write(findSumOfUniquePairs([1, 3, 2, 4], 4) + '<br>'); // 1
document.write(findSumOfUniquePairs([1, 1, 1], 2) + '<br>'); // 1
document.write(findSumOfUniquePairs([1, 1, 1, 1], 2) + '<br>'); // 1
document.write(findSumOfUniquePairs([1, 2, 3, 1, 2, 3, 1], 4) + '<br>'); // 7
document.write(findSumOfUniquePairs([5, 5, 1, 1, 1], 6) + '<br>'); // 2
document.write(findSumOfUniquePairs([0, 5, 0, 5, 1, 1, 1], 6) + '<br>'); // 5
This works, but it mucks up the initial array.
function functionName(arr, arg) {
var newArr = [];
for(var i = 0; i < arr.length; i++){
for(var j = i + 1; j < arr.length; j++) {
if((arr[i] + arr[j]) === arg ) {
newArr.push(i , j);
arr[i] = null;
arr[j] = null;
}
}
}
if(newArr.length === 0) {
return console.log(0);
}
return console.log(newArr.reduce(function(a,b){return a + b}));
}
Solution with loops with restart, if a sum is found. the found summands are stored in usedNumbers and later sorted and used to get the index for summing the index.
The sorting and the last index provides the correct start position for the Array.prototype.indexOf.
Edit:
what about [1,1,1,1], 2 ... should that be 6 or 1? – Jaromanda X 21
#JaromandaX that should be 1, after the pair is found with the values then it shouldn't look for a pair with the same values again – Torean
This version takes care of the requirement.
function f(array, sum) {
var arrayCopy = array.slice(0),
usedNumbers = [],
index = 0,
indexA = 0,
indexB,
a, b;
while (indexA < arrayCopy.length) {
indexB = indexA + 1;
while (indexB < arrayCopy.length) {
a = arrayCopy[indexA];
b = arrayCopy[indexB];
if (a + b === sum) {
usedNumbers.push(a, b);
arrayCopy = arrayCopy.filter(function (i) { return a !== i && b !== i; });
indexA--; // correction to keep the index
break;
}
indexB++;
}
indexA++;
}
return usedNumbers.sort().reduce(function (r, a, i) {
index = array.indexOf(a, i === 0 || a !== usedNumbers[i - 1] ? 0 : index + 1);
return r + index;
}, 0);
}
document.write(f([1, 4, 2, 3, 0, 5], 7) + '<br>'); // 11
document.write(f([1, 1, 1], 2) + '<br>'); // 1
document.write(f([5, 5, 1, 1, 1], 6) + '<br>'); // 2
document.write(f([0, 5, 0, 5, 1, 1, 1], 6) + '<br>'); // 5
document.write(f([1, 1, 1, 1], 2) + '<br>'); // 1
The solution below is very compact. It avoids unnecessary checks and loops only through the relevant elements. You can check the working codepen here:
http://codepen.io/PiotrBerebecki/pen/RRGaBZ.
function pairwise(arr, arg) {
var sum = 0;
for (var i=0; i<arr.length-1; i++) {
for (var j=i+1; j<arr.length; j++) {
if (arr[i] <= arg && arr[j] <= arg && arr[i] + arr[j] == arg) {
sum += i+j;
arr[i] = arr[j] = NaN;
}
}
}
return sum;
}
console.log( pairwise([1, 1, 0, 2], 2) ) // should return 6
Under the hood:
Start looping from the element with index (i) = 0.
Add a second loop only for the elements which are later in the array. Their index j is always higher than i as we are adding 1 to i.
If both elements (numbers) are less than or equal to to the arg, check if their sum equals to the arg. This avoids checking the sum if either of the numbers are greater than the arg.
If the pair has been found then change their values to NaN to avoid further checks and duplication.
This solution should have a time complexity of 0(n) or linear
Much faster than two nested for-loops. This function will give you the two indices that add up to the target number. It can easily be modified to solve any other configuration of this problem.
var twoSum = function(nums, target) {
const hash = {}
for(let i = 0; i < nums.length; i++) {
hash[nums[i]] = i
}
for(let j = 0; j < nums.length; j++) {
let numToFind = target - nums[j]
if(numToFind in hash && hash[numToFind] !== j) {
return [hash[numToFind], j]
}
}
return false
};
console.log(twoSum([1,2,3,5,7], 5))
In Python:
def twoSum(self, nums: List[int], target: int) -> List[int]:
myMap = {}
for i in range(len(nums)):
myMap[nums[i]] = i
for j in range(len(nums)):
numToFind = target - nums[j]
if numToFind in myMap and myMap[numToFind] != j:
return [myMap[numToFind], j]
print(twoSum([1,2,3,5,7], 5))
In Java:
import java.util.*;
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for(Integer i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for(Integer j = 0; j < nums.length; j++) {
Integer numToFind = target - nums[j];
Integer myInt = map.get(numToFind);
if(map.containsKey(numToFind) && myInt != j) {
return new int[] {myInt , j};
}
}
return new int[] {0, 0};
}
}
System.out.println(twoSum([1,2,3,5,7], 5))