Related
I am trying to generate sprial matrix in javascript.
question
Given an integer A, generate a square matrix filled with elements from 1 to A^2 in spiral order.
input : 3
[ [ 1, 2, 3 ],
[ 8, 9, 4 ],
[ 7, 6, 5 ] ]
when input is 4
[ [1, 2, 3, 4],
[12, 13, 14, 5],
[11, 16, 15, 6],
[10, 9, 8, 7] ]
my approach is to create 2d array with 0 value and after that they will fill values.
let generateMatrix = function(A) {
let arr = [], counter = 1;
for (let i = 0; i < A; i++) {
let items = []
for (let j = 0; j < A; j++) {
items.push(0)
}
arr.push(items)
}
var spiralMatrix = function(arr) {
if (arr.length > 1) {
for (let i = 0; i < arr[0].length; i++) {
arr[0][i] = counter++;
}
}
return arr
}
return spiralMatrix(arr)
}
console.log(generateMatrix(2))
You could take loops for each edges and loop until no more ranges are avaliable.
function spiral(length) {
var upper = 0,
lower = length - 1,
left = 0,
right = length - 1,
i = 0,
j = 0,
result = Array.from({ length }, _ => []),
value = 1;
while (true) {
if (upper++ > lower) break;
for (; j < right; j++) result[i][j] = value++;
if (right-- < left) break;
for (; i < lower; i++) result[i][j] = value++;
if (lower-- < upper) break;
for (; j > left; j--) result[i][j] = value++;
if (left++ > right) break;
for (; i > upper; i--) result[i][j] = value++;
}
result[i][j] = value++;
return result;
}
var target = document.getElementById('out'),
i = 10;
while (--i) target.innerHTML += spiral(i).map(a => a.map(v => v.toString().padStart(2)).join(' ')).join('\n') + '\n\n';
<pre id="out"></pre>
This bit of code should do what you are trying to.
// This is your Editor pane. Write your JavaScript hem and
// use the command line to execute commands
let generateMatrix = function(A) {
let arr = [],
counter = 1;
for (let i = 0; i < A; i++) {
let items = [];
for (let j = 0; j < A; j++) {
items.push(0);
}
arr.push(items);
}
var spiralMatrix = function(arr) {
let count = 1;
let k = 0; // starting row
let m = arr.length; // ending row
let l = 0; // starting column
let n = arr[0].length; //ending column
while (k < m && l < n) {
// top
for (var i = l; i < n; i++) {
arr[k][i] = count;
count++;
}
k++;
// right
for (var i = k; i < m; i++) {
arr[i][n - 1] = count;
count++;
}
n--;
// bottom
if (k < m) {
for (var i = n - 1; i >= l; i--) {
arr[m - 1][i] = count;
count++;
}
m--;
}
// left
if (l < n) {
for (var i = m - 1; i >= k; i--) {
arr[i][l] = count;
count++;
}
l++;
}
}
return arr;
};
return spiralMatrix(arr);
};
console.log(generateMatrix(4));
This is in some ways the reverse of an answer I gave to another question. We can recursively build this up by slicing out the first row and prepending it to the result of rotating the result of a recursive call on the remaining numbers:
const reverse = a =>
[...a] .reverse ();
const transpose = m =>
m [0] .map ((c, i) => m .map (r => r [i]))
const rotate = m =>
transpose (reverse (m))
const makeSpiral = (xs, rows) =>
xs .length < 2
? [[... xs]]
: [
xs .slice (0, xs .length / rows),
... rotate(makeSpiral (xs .slice (xs .length / rows), xs.length / rows))
]
const range = (lo, hi) =>
[...Array (hi - lo + 1)] .map ((_, i) => lo + i)
const generateMatrix = (n) =>
makeSpiral (range (1, n * n), n)
console .log (generateMatrix (4))
A sharp eye will note that rotate is different here from the older question. transpose (reverse (m)) returns a clockwise rotated version of the input matrix. reverse (transpose (m)) returns a counter-clockwise rotated one. Similarly, here we rotate the result of the recursive call before including it; whereas in the other question we recurse on the rotated version of the matrix. Since we're reversing that process, it should be reasonably clear why.
The main function is makeSpiral, which takes an array and the number of rows to spiral it into and returns the spiraled matrix. (If rows is not a factor of the length of the array, the behavior might be crazy.) generateMatrix is just a thin wrapper around that to handle your square case by generating the initial array (using range) and passing it to makeSpiral.
Note how makeSpiral works with rectangles other than squares:
makeSpiral ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 2) //=>
// [
// [ 1, 2, 3, 4, 5, 6],
// [12, 11, 10, 9, 8, 7]
// ]
makeSpiral ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 3) //=>
// [
// [ 1, 2, 3, 4],
// [10, 11, 12, 5],
// [ 9, 8, 7, 6]
// ]
makeSpiral ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 4) //=>
// [
// [ 1, 2, 3],
// [10, 11, 4],
// [ 9, 12, 5],
// [ 8, 7, 6]
// ]
makeSpiral ([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], 6) //=>
// [
// [ 1, 2],
// [12, 3],
// [11, 4],
// [10, 5],
// [ 9, 6],
// [ 8, 7]
// ]
The other functions -- range, reverse, transpose, and rotate -- are general purpose utility functions for working with arrays or matrices.
Here's one solution.
I keep the current "moving direction" in dx and dy, such that the next matrix element indices are given by x+dx and y+dy.
If the next item is already filled or is out of bounds, I change this direction clockwise. Otherwise, I fill it with the next value.
const size = 6;
const matrix = Array(size).fill().map(() => Array(size).fill(0));
let x = -1;
let y = 0;
let dx = 1;
let dy = 0;
function changeDirection() {
if (dx === 1) {
dx = 0;
dy = 1;
} else if (dy === 1) {
dy = 0;
dx = -1;
} else if (dx === -1) {
dx = 0;
dy = -1;
} else {
dx = 1;
dy = 0;
}
}
for (let i = 0; i < size * size; i++) {
const yNext = y + dy;
const xNext = x + dx;
const nextRow = matrix[yNext] || [];
const nextItemContent = nextRow[xNext];
if (nextItemContent === undefined || nextItemContent > 0) {
changeDirection();
i--;
continue;
}
y = yNext;
x = xNext;
matrix[y][x] = i + 1;
}
const result = document.getElementById('result');
matrix.forEach(row => {
row.forEach(value => {
result.innerHTML += value.toString().padStart(3);
});
result.innerHTML += '\n';
});
<pre id="result"></pre>
I'm calculating the index, each number should go in a linear array
console.clear();
Array.prototype.range = function(a, b, step) {
step = !step ? 1 : step;
b = b / step;
for(var i = a; i <= b; i++) {
this.push(i*step);
}
return this;
};
const spiral = function(dimen) {
"use strict";
const dim = dimen;
const dimw = dim;
const dimh = dim;
var steps = [1, dimh, -1, -dimh];
var stepIndex = 0;
var count = 1;
var countMax = dimw
var dec = 0
var index = 0;
var arr = [];
arr = arr.range(1, dimh * dimw)
const newArr = arr.reduce((coll, x, idx) => {
index += steps[stepIndex]
coll[index-1] = idx+1;
if (count === countMax) {count = 0; stepIndex++; dec++;}
if (dec === 1) {dec = -1; countMax--}
if (stepIndex == steps.length) {stepIndex = 0}
count++;
return coll;
}, []);
var ret = []
while (newArr.length) {
ret.push(newArr.splice(0,dimw))
}
return ret
}
console.log(spiral(3))
console.log(spiral(4))
console.log(spiral(5))
var n=14; // size of spiral
var s=[]; // empty instruction string
function emp() {} // no move
function xpp() {xp++;} // go right
function xpm() {xp--;} // go left
function ypp() {yp++;} // go down
function ypm() {yp--;} // go up
var r=[xpp,ypp,xpm,ypm]; // instruction set
s.push(emp); // push 'no move' (used for starting point)
var c=n-1;
while (c-->0) s.push(r[0]); // push first line - uses a different rule
for (var i=1;i<2*n-1;i++) { // push each leg
c=Math.floor((2*n-i)/2);
while (c-->0) s.push(r[i%4]);
}
var sp=new Array(n); // spiral array
for (var i=0;i<n;i++) sp[i]=new Array(n);
var xp=0; // starting position
var yp=0;
for (var i=0;i<n*n;i++) {
s[i](); // execute next instruction
sp[yp][xp]=i+1; // update array
}
for (var i=0;i<n;i++) console.log(sp[i].toString()); // log to console
This code makes a macro of functions to generate a run sequence, for example:
'right4, down4, left4, up3, right3, down2, left2, up1, right1
and then implements it.
Here is a solution to Spiral Matrix from leetcode, maybe this can help
https://leetcode.com/problems/spiral-matrix/
var spiralOrder = function(matrix) {
if (matrix.length == 0) {
return [];
}
let result = [];
let rowStart = 0;
let rowEnd = matrix.length - 1;
let colStart = 0;
let colEnd = matrix[0].length - 1;
while (true) {
// top
for (let i = colStart; i <= colEnd; i++) {
result.push(matrix[rowStart][i]);
}
rowStart++;
if (rowStart > rowEnd) {
return result;
}
// right
for (let i = rowStart; i <= rowEnd; i++) {
result.push(matrix[i][colEnd]);
}
colEnd--;
if (colEnd < colStart) {
return result;
}
// bottom
for (let i = colEnd; i >= colStart; i--) {
result.push(matrix[rowEnd][i]);
}
rowEnd--;
if (rowEnd < rowStart) {
return result;
}
// left
for (let i = rowEnd; i >= rowStart; i--) {
result.push(matrix[i][colStart]);
}
colStart++;
if (colStart > colEnd) {
return result;
}
}
return result;
};
console.log(
spiralOrder([[2, 3, 4], [5, 6, 7], [8, 9, 10], [11, 12, 13], [14, 15, 16]])
);
console.log(spiralOrder([[7], [9], [6]]));
console.log(spiralOrder([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]));
console.log(spiralOrder([[1, 2, 3], [4, 5, 6], [7, 8, 9]]));
Here's my answer using only one for loop -
function matrix(n) {
const arr = [];
let row = 0;
let column = 0;
let counter = 1;
let edge = n - 1;
let leftToRightRow = false;
let topToBottomCol = false;
let rightToLeftRow = false;
let bottomToTopCol = false;
for (i = 0; i < n * n; i++) {
if (column <= edge && !leftToRightRow) {
if (!Array.isArray(arr[row])) {
arr[row] = []; // if array is not present at this index, then insert one
}
arr[row][column] = counter;
if (column == edge) {
row = row + 1;
leftToRightRow = true;
} else {
column = column + 1;
}
counter = counter + 1;
} else if (column === edge && !topToBottomCol) {
if (!Array.isArray(arr[row])) {
arr[row] = []; // if array is not present at this index, then insert one
}
arr[row][column] = counter;
if (row === edge) {
column = column - 1;
topToBottomCol = true;
} else {
row = row + 1;
}
counter = counter + 1;
} else if (column >= 0 && !rightToLeftRow) {
arr[row][column] = counter;
if (column === 0) {
row = row - 1;
rightToLeftRow = true;
} else {
column = column - 1;
}
counter = counter + 1;
} else if (row >= n - edge && !bottomToTopCol) {
arr[row][column] = counter;
if (row === n - edge) {
column = column + 1;
bottomToTopCol = true;
//setting these to false for next set of iteration
leftToRightRow = false;
topToBottomCol = false;
rightToLeftRow = false;
edge = edge - 1;
} else {
row = row - 1;
}
counter = counter + 1;
}
}
return arr;
}
Solution is implemented in C++, but only logic matter then you can do it in any language:
vector<vector<int> > Solution::generateMatrix(int A) {
vector<vector<int>> result(A,vector<int>(A));
int xBeg=0,xEnd=A-1;
int yBeg=0,yEnd=A-1;
int cur=1;
while(true){
for(int i=yBeg;i<=yEnd;i++)
result[xBeg][i]=cur++;
if(++xBeg>xEnd) break;
for(int i=xBeg;i<=xEnd;i++)
result[i][yEnd]=cur++;
if(--yEnd<yBeg) break;
for(int i=yEnd;i>=yBeg;i--)
result[xEnd][i]=cur++;
if(--xEnd<xBeg) break;
for(int i=xEnd;i>=xBeg;i--)
result[i][yBeg]=cur++;
if(++yBeg>yEnd) break;
}
return result;
}
Solition in c#:
For solving this problem we use loops for each moving directions
public IList<int> SpiralOrder(int[][] matrix) {
var result = new List<int>();
var n = matrix[0].Length;
var m = matrix.Length;
var i = 0;
var j = 0;
var x = 0;
var y = 0;
while (true)
{
//left to right moving:
while (x <= n - 1 - i)
{
result.Add(matrix[y][x]);
x++;
}
if (result.Count == n * m)
return result;
x--;y++;
//up to down moving:
while (y <= m - 1 - j)
{
result.Add(matrix[y][x]);
y++;
}
if (result.Count == n * m)
return result;
y--;x--;
//right to left moving:
while (x >= j)
{
result.Add(matrix[y][x]);
x--;
}
if (result.Count == n * m)
return result;
x++;y--;
//down to up moving:
while (y > j)
{
result.Add(matrix[y][x]);
y--;
}
if (result.Count == n * m)
return result;
y++;x++;
i++;
j++;
}
}
Introduction
The problem is explained on Link to challenge for best instructions
As far as I understand, the idea is to swap elements on the left side of an array with elements from the right side of an array if the element on the left side is greater than 0.
I.e. [2, -4, 6, -6] => [-6, -4, 6, 2].
Positives on the left side have to get swapped with their corresponding "out of place" opposite on the right side. So, unfortunately we can't use
array = array.sort((a, b) => a - b);
In the end, there should be only negatives numbers on the left side and positives on the right side. If the array has an odd length, then one "side" will be one (or more) elements larger, depending on whether or not there are more positives or negatives in the original array.
I.e. [8, 10, -6, -7, 9, 5] => [-7, -6, 10, 8, 9, 5]
Approach
To solve this, I created a pretty lengthy algorithm that splits the array into halfs, and keeps track of "out of place" (positive) elements on the left side and "out of place" (negative) elements on the right side, to use for swapping:
function wheatFromChaff (values) {
//IF ORIGINAL VALUES LENGTH IS EVEN
if (values.length % 2 === 0) {
let left = values.slice(0, values.length / 2);
let right = values.slice(values.length / 2);
let outOfPlaceLeft = left.filter((element) => element > 0);
let outOfPlaceRight = right.filter((element) => element < 0);
//replace positive "out of place" left elements with negative "out of place" right elements
for (let i = 0; i < left.length; i++) {
if (left[i] > 0) {
left.splice(i, 1, outOfPlaceRight.pop());
}
}
//push remaining "out of place" negative right elements to the left
while (outOfPlaceRight.length) {
let first = outOfPlaceRight.shift();
left.push(first);
}
//filter out any negatives on the right
right = right.filter((element) => element > 0).concat(outOfPlaceLeft);
//concat the remaining positives and return
return left.concat(right);
}
//IF ORIGINAL VALUES LENGTH IS ODD
if (values.length % 2 !== 0) {
let left2 = values.slice(0, Math.floor(values.length / 2));
let right2 = values.slice(Math.ceil(values.length / 2));
let middle = values[Math.floor(values.length/2)];
let outOfPlaceLeft2 = left2.filter((element) => element > 0);
let outOfPlaceRight2 = right2.filter((element) => element < 0);
//replace "out of place", positive left elements
for (let j = 0; j < left2.length; j++) {
//if out of there are out of place elements on the right
if (outOfPlaceRight2.length) {
if (left2[j] > 0) {
left2.splice(j, 1, outOfPlaceRight2.pop());
}
}
//if out of place elements on the right are empty
if (!outOfPlaceRight2.length && middle < 0) {
if (left2[j] > 0) {
left2.splice(j, 1, middle);
}
}
}
//filter out negatives on the right
right2 = right2.filter((element) => element > 0);
//unshift remaining "out of place" positive left elements to the right
while (outOfPlaceLeft2.length) {
let first = outOfPlaceLeft2.shift();
right2.unshift(first);
}
if (middle > 0) {
right2.unshift(middle);
}
if (middle < 0) {
left2.push(middle);
}
return left2.concat(right2);
}
}
console.log(wheatFromChaff([2, -6, -4, 1, -8, -2]));
Sometimes the previous approach works, but it gives me an error on Codewars:
AssertionError [ERR_ASSERTION]: [ -10, 7 ] deepEqual [ -10, -3, 7 ]
Do you see any flaws with my logic? Any suggestions for a better strategy?
I have solved this using two "pointers" one starting on the head of the array, and the other starting at the tail of the array. The idea is, on every iteration, to increment the head or decrement the tail iteratively until you found a positive number on the head pointer and a negative number at the tail pointer. When this condition is satisfied, then you have to do a swap on the positions of the elements and continue. Finally, you have to stop the loop when the head pointer is greater than the tail pointer.
function wheatFromChaff(values)
{
let res = [];
let head = 0, tail = values.length - 1;
while (head <= tail)
{
if (values[head] < 0)
{
res[head] = values[head++];
}
else if (values[tail] > 0)
{
res[tail] = values[tail--];
}
else
{
res[tail] = values[head];
res[head++] = values[tail--];
}
}
return res;
}
console.log(wheatFromChaff([2, -4, 6, -6]));
console.log(wheatFromChaff([8, 10, -6, -7, 9]));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
This approach passed all test mentioned on that link:
Time: 894ms Passed: 5 Failed: 0
But maybe there is a better solution.
Another way, using indexOf() and lastIndexOf() helper functions:
function lastIndexOf(arr, pred, start) {
start = start || arr.length - 1;
for (let i = start; i >= 0; i--) {
if (pred(arr[i])) return i;
}
return -1;
}
function indexOf(arr, pred, start = 0) {
for (let length = arr.length, i = start; i < length; i++) {
if (pred(arr[i])) return i;
}
return Infinity;
}
function wheatFromChaff(values) {
let firstPos = indexOf(values, x => x > 0);
let lastNeg = lastIndexOf(values, x => x < 0);
while ( firstPos < lastNeg ) {
let a = values[firstPos];
let b = values[lastNeg];
values[firstPos] = b;
values[lastNeg] = a;
firstPos = indexOf(values, x => x > 0, firstPos);
lastNeg = lastIndexOf(values, x => x < 0, lastNeg);
}
return values;
}
console.log(wheatFromChaff([2, -6, -4, 1, -8, -2]));
I am looking for an implementation in JavaScript for the following problem.
Consider a sorted array:
[1,2,5,9,10,12,20,21,22,23,24,26,27]
I would like to calculate the length of the maximum range that increased by 1, duplicates are not allowed.
The given example has the following ranges:
1,2
9,10
20,21,22,23,24 // the maximum range
26,27
So the return value for the given example should be 5.
I know how to solve this problem with the obvious solution, but I believe it is possible to solve the problem with more efficient and short algorithm.
A short solution
I don't think this is any more efficient than what pretty much everybody else has suggested, but the code is reasonably short and only loops over the array once, except for the first element. Not sure if it's any help:
var arr = [1, 2, 5, 9, 10, 12, 20, 21, 22, 23, 24, 26, 27];
var streak = 0, best = 0, bestStart;
for (var i = 1; i < arr.length; i++) {
if(arr[i]-arr[i-1] === 1) streak++;
else streak = 0;
if (streak > best) [best, bestStart] = [streak, i - streak];
}
var bestArr = arr.slice(bestStart, bestStart + best + 1);
console.log('Best streak: '+bestArr);
Speeding it up
After looking at the code, I realized that there is a way to speed it up slightly, by not checking the last few elements of the array, based on the previous value of best:
var arr = [1, 2, 5, 9, 10, 12, 20, 21, 22, 23, 24, 26, 27];
var streak = 0, best = 0, bestStart;
for (var i = 1; i < arr.length; i++) {
if(best > arr.length - i + streak) break;
if(arr[i]-arr[i-1] === 1) streak++;
else streak = 0;
if (streak > best) [best, bestStart] = [streak, i - streak];
}
var bestArr = arr.slice(bestStart, bestStart + best + 1);
console.log('Best streak: '+bestArr);
One possible solution would be to iterate the array, keeping the the current range as long as the numbers are successors. If the next number is not a successor of the previous number, close the current range and store its length - by comparing it to the length of the last range.
In this approach, the array is iterated only once and the maximum found length of a range is updated in constant time, yielding an O(n) algorithm where n is the number of elements in the input.
An implementation in C#-like pseudocode could be as follows.
int MaximumLength = minus infinity
int CurrentValue = Input[0];
int CurrentLength = 1;
for(int i = 1; i < Input.Length; i++)
{
if ( CurrentValue + 1 == Input[i] )
{
// same range
CurrentLength = CurrentLength + 1;
}
else
{
// new range
MaximumLength = Math.Max(MaximumLength, CurrentLength);
CurrentLength = 1;
}
CurrentValue = Input[i];
}
// check current length again after loop termination
MaximumLength = Math.Max(MaximumLength, CurrentLength);
It is impossible to obtain better than O(n) because the input cannot be read in less than O(n) time. If that would be possible, it would imply that there are instances for which the result does not depend on every element of the input, which is not the case for the given problem. The algorithm Philipp Maurer has sketched below would also yield an O(n) runtime bound if the maximum range length is 1, i.e. no adjacent numbers in the input are successors.
Something like this should find the maximum length first and not last.
Let max = 0
Let n = array length
While n > 2
Let m = 0
While m <= (array length - n)
Let first = m
Let last = m + n - 1
Let diff = (value of element 'last' in array) - (value of element 'first' in array)
if diff = n - 1 then
max = n
stop
end if
Increase m
end while
Decrease n
end while
Edit (javascript implementation)
var a = [1,2,5,9,10,12,20,21,22,23,24,26,27];
var max = 1;
var n = a.length;
while(n > 2) {
var m = 0;
while(m <= a.length - n)
{
var first = m;
var last = m + n - 1;
var diff = a[last] - a[first];
if (diff == n - 1 && diff > max) {
max = n;
break;
}
m++;
}
n--;
}
console.log(max);
JSFiddle
I think looping and comparing with stored previous maximum length is optimal solution. Maybe like this:
function findLongestRange(input) {
let maxLength = 0
let currentLength = 0
for (let i = 0; i < input.length; i++) {
if (i !== input.length) {
if (input[i] === input[i + 1] - 1) {
currentLength++
} else {
if (maxLength <= currentLength && currentLength !== 0) {
maxLength = currentLength + 1
}
currentLength = 0
}
}
}
return maxLength
}
const data = [1, 2, 5, 9, 10, 12, 20, 21, 22, 23, 24, 26, 27]
console.log(findLongestRange(data))
Here is the version with tests to check how it works with different input.
const data = [1, 2, 5, 9, 10, 12, 20, 21, 22, 23, 24, 26, 27]
function findLongestRange(input) {
let maxLength = 0
let currentLength = 0
for (let i = 0; i < input.length; i++) {
if (i !== input.length) {
if (input[i] === input[i + 1] - 1) {
currentLength++
} else {
if (maxLength <= currentLength && currentLength !== 0) {
maxLength = currentLength + 1
}
currentLength = 0
}
}
}
return maxLength
}
console.clear()
;[
[[1,2,5,6,7,1,2], 3],
[[], 0],
[data, 5],
[[1,2,3], 3],
[[1,3,4,6,8,1], 2],
[[1,3,5], 0],
].forEach((test, index) => {
const result = findLongestRange(test[0])
console.assert(result === test[1], `Fail #${index}: Exp: ${test[1]}, got ${result}`)
})
A Python answer:
l = [1,2,5,9,10,12,20,21,22,23,24,26,27]
current_range = None
current_range_val = 0
max_range = 0
max_range_val = 0
for i, j in zip(l, l[1:]):
if j - i == 1:
current_range_val += 1
if current_range is None:
current_range = (i, j)
current_range = (current_range[0], j)
else:
if current_range_val > max_range_val:
max_range = current_range
max_range_val = current_range_val
current_range_val = 0
current_range = (j, None)
print(max_range)
gives
(20, 24)
Say I have a list [1,2,3,4,5,6,7]
and I would like to find the closest sum of numbers to a given number. Sorry for the crappy explanation but here's an example:
Say I have a list [1,2,3,4,5,6,7] I want to find the closest numbers to, say, 10.
Then the method should return 6 and 4 or 7 and 3 because its the closest he can get to 10. So 5 + 4 would be wrong because thats 9 and he can make a 10.
another example : you want the closest to 14 , so then he should return 7 and 6
If you got any questions plz ask because its difficult to explain what I want :P
Functions for combine, locationOf, are taken from different answers, written by different authors.
printClosest([0.5,2,4] , 5);
printClosest([1, 2, 3, 4, 5, 6, 7], 28);
printClosest([1, 2, 3, 4, 5, 6, 7], 10.9);
printClosest([1, 2, 3, 4, 5, 6, 7], 10, 2);
printClosest([1, 2, 3, 4, 5, 6, 7], 10, 3);
printClosest([1, 2, 3, 4, 5, 6, 7], 14, 2);
function printClosest(array, value, limit) {
var checkLength = function(array) {
return array.length === limit;
};
var combinations = combine(array); //get all combinations
combinations = limit ? combinations.filter(checkLength) : combinations;//limit length if required
var sum = combinations.map(function(c) { //create an array with sum of combinations
return c.reduce(function(p, c) {
return p + c;
}, 0)
});
var sumSorted = sum.slice(0).sort(function(a, b) {//sort sum array
return a - b;
});
index = locationOf(value, sumSorted);//find where the value fits in
//index = (Math.abs(value - sum[index]) <= Math.abs(value - sum[index + 1])) ? index : index + 1;
index = index >= sum.length ? sum.length - 1 : index;
index = sum.indexOf(sumSorted[index]);//get the respective combination
console.log(sum, combinations, index);
document.getElementById("result").innerHTML += "value : " + value + " combi: " + combinations[index].toString() + " (limit : " + (limit || "none") + ")<br>";
}
function combine(a) {
var fn = function(n, src, got, all) {
if (n == 0) {
if (got.length > 0) {
all[all.length] = got;
}
return;
}
for (var j = 0; j < src.length; j++) {
fn(n - 1, src.slice(j + 1), got.concat([src[j]]), all);
}
return;
}
var all = [];
for (var i = 0; i < a.length; i++) {
fn(i, a, [], all);
}
all.push(a);
return all;
}
function locationOf(element, array, start, end) {
start = start || 0;
end = end || array.length;
var pivot = parseInt(start + (end - start) / 2, 10);
if (end - start <= 1 || array[pivot] === element) return pivot;
if (array[pivot] < element) {
return locationOf(element, array, pivot, end);
} else {
return locationOf(element, array, start, pivot);
}
}
<pre id="result"><pre>
var data= [1, 2, 3,4,5,6,7];
var closest = 14;
for (var x = 0; x < data.length; x++) {
for (var y = x+1; y < data.length; y++) {
if(data[x] + data[y] == closet){
alert(data[x].toString() + " " + data[y].toString());
}
}
}
From what I understood from your question, I made this snippet. I assumed you did not wanted to have the same digit twice (e.g 14 => 7 + 7).
It is working with your examples.
var arr = [1, 2, 3, 4, 5, 6, 7];
var a = 0, b = 0;
var nb = 14;
for(var i in arr) {
for(var j in arr) {
if(i != j) {
var tmp = arr[i] + arr[j];
if(tmp <= nb && tmp > a + b) {
a = arr[i];
b = arr[j];
}
}
}
}
document.write("Closest to " + nb + " => " + a + " + " + b);
I have a little bit long winded solution to the problem just so it is easier to see what is done.
The main benefits with solution below:
The second loop will not start from beginning of the array again. What I mean that instead of having loop_boundary for second loop as 0 as you normally would, here it starts from next index. This helps if your numbers array is long. However, if it as short as in example, the impact in performance is minimal. Decreasing first loop's boundary by one will prevent errors from happening.
Works even when the wanted number is 1 or negative numbers.
Fiddle:
JSFiddle
The code:
var numbers = [1,2,3,4,5,6,7];
var wanted_number = 1;
var closest_range, closest1, closest2 = null;
var loop1_boundary = numbers.length-1;
for(var i=0; i<loop1_boundary; i++) {
var start_index = i+1;
var loop2_boundary = numbers.length;
for(var k=start_index; k<loop2_boundary; k++) {
var number1 = parseInt(numbers[i]);
var number2 = parseInt(numbers[k]);
var sum = number1 + number2;
var range = wanted_number - sum;
document.write( number1+' + '+number2 +' < '+closest_range+'<br/>' );
if(Math.abs(range) < Math.abs(closest_range) || closest_range == null ) {
closest_range = range;
closest1 = number1;
closest2 = number2;
}
}
if(range==0){
break;
}
}
document.write( 'closest to given number was '+closest1+' and '+closest2+'. The range from wanted number is '+closest_range );
This proposal generates all possible combinations, collects them in an object which takes the sum as key and filters then the closest sum to the given value.
function getSum(array, sum) {
function add(a, b) { return a + b; }
function c(left, right) {
var s = right.reduce(add, 0);
if (s > sum) {
return;
}
if (!result.length || s === result[0].reduce(add, 0)) {
result.push(right);
} else if (s > result[0].reduce(add, 0)) {
result = [right];
}
left.forEach(function (a, i) {
var x = left.slice();
x.splice(i);
c(left.slice(0, i), [a].concat(right));
});
}
var result = [];
c(array, [], 0);
return result;
}
function print(o) {
document.write('<pre>' + JSON.stringify(o, 0, 4) + '</pre>');
}
print(getSum([1, 2, 3, 4, 5, 6, 7], 10));
print(getSum([1, 2, 3, 4, 5, 6, 7], 14));
print(getSum([1, 2, 3, 4, 5, 6, 7], 19));
There is an array of numbers [1,2,3,4,5,6,7,8,9,10]
I need to get all numbers from this sequence that are different from current for more than 2 items, but looped.
For example if current number is one, so new list should have everything except 9,10,1,2,3, or if current number is four so new list should be everything except 2,3,4,5,6.
Is there any technique how to make this, without creating multiple loops for items at start and at the end?
Thank you.
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var exclude = function (start, distance, array) {
var result = [];
for (var i = 0; i < array.length; i++) {
var d = Math.min(
Math.abs(start - i - 1),
Math.abs(array.length + start - i - 1)
)
if (d > distance) {
result.push(array[i]);
}
}
return result;
}
I think this performs what you asked:
// Sorry about the name
function strangePick(value, array) {
var n = array.length
, i = array.indexOf(value);
if (i >= 0) {
// Picked number
var result = [value];
// Previous 2 numbers
result.unshift(array[(i + n - 1) % n]);
result.unshift(array[(i + n - 2) % n]);
// Next 2 numbers
result.push(array[(i + 1) % n]);
result.push(array[(i + 2) % n]);
return result;
} else {
return [];
}
}
Some tests:
var array = [1,2,3,4,5,6,7,8,9,10];
console.log(strangePick(1, array)); // [9,10,1,2,3]
console.log(strangePick(4, array)); // [2,3,4,5,6]
You may use javascript array.slice:
function get_offset_sequence(arr, index, offset) {
var result = [];
if (index - offset < 0) {
result = arr.slice(index - offset).concat(arr.slice(0, index + offset + 1));
}
else if (index + offset > arr.length - 1) {
result = arr.slice(index - offset).concat(arr.slice(0, Math.abs(arr.length - 1 - index - offset)));
}
else {
result = arr.slice(index - offset, index + offset + 1)
}
return result;
}
Example of use:
var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var index = 1;
var offset = 2;
for (var i=0; i < 10; i++) { console.log(i, arr[i], get_offset_sequence(arr, i, offset)) }