Weiszfeld's algorithm -- incorrect answer despite perfect guess - javascript

I'm trying to implement Weiszfeld's algorithm to find the geometric median of a set of points. Below is what I've come up with, but gives the incorrect answer of
[
0,
0,
12
]
For this test case:
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 12]
]
The answer should be [0,0,0]. The initial "guess" (computed by findClosest(points, findMean(points))) is actually correct, but then Weiszfeld's algorithm goes and messes it up. Seems to want to converge on [0,0,12] but that would be wrong. The distance between [0,0,12] and the other 2 points is 24, whereas if it had picked one of the 0 points the total distance would been 12.
How can I fix this to give more accurate results?
function calcSquareDist(a, b) {
return a.reduce((prev, curr, idx) => prev + (curr - b[idx]) ** 2, 0);
}
function calcDist(a, b) {
return Math.sqrt(calcSquareDist(a, b));
}
function findClosest(points, target) {
let min = {
index: -1,
value: Infinity
};
for (const [i, c] of points.entries()) {
const d = calcSquareDist(c, target);
if (d < min.value) {
min = {
index: i,
value: d
};
}
}
return points[min.index];
}
function weightedAverage(points, w) {
let n = points.length;
let weightedSum = Array(points[0].length).fill(0); // initialize a vector of zeroes
let weightSum = 0;
for (let i = 0; i < n; i++) {
for (let j = 0; j < points[i].length; j++) {
weightedSum[j] += w[i] * points[i][j]; // add the weighted point to the weighted sum
}
weightSum += w[i]; // add the weight to the weight sum
}
return weightedSum.map((sum) => sum / weightSum); // return the weighted average
}
const EPSILON = 1e-6;
function findMean(points) {
if (!(points === null || points === void 0 ? void 0 : points.length))
throw new Error("points is empty");
let sum = Array(points[0].length).fill(0);
for (const p of points) {
for (const [i, v] of p.entries()) {
sum[i] += v;
}
}
return sum.map(v => v / points.length);
}
function geometricMedian(points) {
if (!(points === null || points === void 0 ? void 0 : points.length))
throw new Error("points is empty");
if (points.length === 1) {
return points[0];
}
let x = findClosest(points, findMean(points));
for (let i = 0; i < 25; ++i) {
const weights = points.map(p => calcDist(p, x));
const x_new = weightedAverage(points, weights);
if (calcDist(x, x_new) < EPSILON) {
return x_new;
}
x = x_new;
}
return x;
}
console.log(geometricMedian([
[0, 0, 0],
[0, 0, 0],
[0, 0, 12]
]))
I transpiled the above from TypeScript so that it can executed on StackOverflow. Here's the original code on TS playground.

Related

i cant get the right result of the contiguous subarray of arr with the maximal sum of items

so i found a solution for this mathematical problem which is this one
function getMaxSubSum(arr) {
let maxSum = 0;
let partialSum = 0;
for (let item of arr) { // for each item of arr
partialSum += item; // add it to partialSum
maxSum = Math.max(maxSum, partialSum); // remember the maximum
if (partialSum < 0) partialSum = 0; // zero if negative
}
return maxSum;
}
alert ( getMaxSubSum([1,-2,3,9,-9,6]) )
BUT i want to achieve it with another way and im trying this code
function kadane () {
arr = [1,-2,3,9,-9,6]
let maxSub = maxGlobal = arr[0]
for (i=3 ; i<arr.length-1; i++ ) {
maxSub = Math.max(arr[i], maxSub + arr[i])
if (maxSub > maxGlobal) {
maxSub = maxGlobal
}
}
return maxSub
}
alert (kadane())
does anyone know what im doing wrong?
Your solution were really close !
Here you inverted maxSub and maxGlobal in the if section.
Also, I don't know why but your for loop where starting at 3 instead of 1.
Here is your working example
function kadane(arr) {
let maxSub = arr[0]
let maxGlobal = arr[0]
for (i = 1; i < arr.length; i++) {
maxSub = Math.max(arr[i], maxSub + arr[i])
if (maxSub > maxGlobal) {
maxGlobal = maxSub
}
}
return maxGlobal
}
const arr = [1, -2, 3, 9, -9, 6]
alert(kadane(arr))
A little more...
Also, note that you could also have checked the maximum of 2 numbers consecutively.
Example using Array#reduce
function kadane(arr) {
return arr.reduce((acc, curr, index) => {
if(index === 0) return curr > 0 ? curr : 0
else {
const sum = curr + arr[index-1]
return sum > acc ? sum : acc
}
}, 0)
}
console.log(kadane([1, -2, 3, 9, -9, 6]))
console.log(kadane([-1, -2, -3, -4, -5, -6]))

how to generate spiral matrix in javascript?

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++;
}
}

How can i calculate the modes of an array and ignore a data

I'm trying to calculate the mode of an array which I can do, but I want to exclude the 0
This is my code :
const datas = [0, 0, 0, 4, 4, 2, 3, 2, 0];
function mode(numbers) {
var modes = [],
count = [],
i,
number,
maxIndex = 0;
for (i = 0; i < numbers.length; i += 1) {
number = numbers[i];
count[number] = (count[number] || 0) + 1;
if (count[number] > maxIndex) {
maxIndex = count[number];
}
}
for (i in count)
if (count.hasOwnProperty(i)) {
if (count[i] === maxIndex) {
modes.push(Number(i));
}
}
return modes;
}
mode(datas); // output : [0] and I want [4] [2]
Thanks for you time.
You can simply filter out zeroes:
datas = [0, 0, 0, 4, 4, 2, 3, 2, 0];
function mode(numbers) {
// we don't want to consider zeros
// so filter them out
numbers = numbers.filter(function(n) { return n !== 0 });
var modes = [],
count = [],
i, number, maxIndex = 0;
for (i = 0; i < numbers.length; i += 1) {
number = numbers[i];
count[number] = (count[number] || 0) + 1;
if (count[number] > maxIndex) {
maxIndex = count[number];
}
}
for (i in count)
if (count.hasOwnProperty(i)) {
if (count[i] === maxIndex) {
modes.push(Number(i));
}
}
return modes;
}
console.log(mode(datas)) // output : [4] [2]
If you're using ES6, you can use the arrow function syntax:
numbers = numbers.filter(n => n !== 0);
I just wanted to share a way to calculate the mode without for/forEach loops.
Relevant docs:
filter
reduce
Object.keys
Object.values
spread syntax (...)
arrow functions
Math.max
const counted = [0, 0, 0, 4, 4, 2, 3, 2, 0]
.filter(element => element)
.reduce(
(accumulator, currentValue) =>
({
...accumulator,
[currentValue]: (accumulator[currentValue] || 0) + 1
}),
{}
);
const maxCount = Math.max(...Object.values(counted));
const mode = Object.keys(counted).filter(key => counted[key] === maxCount);
console.log(mode);

Fill missing numeric values in an array

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.

Highest Product you can get from k of the integers

I am working on the following problem:
Given an arrayOfInts, find the highestProduct you can get from k of the integers.
This is the solution I have come up with so far based on a generalization of getting the highestProduct from 3 of the integers.
var getHighestProductOfk = function (arrayOfInts, k) {
if (arrayOfInts.length < k) {
throw Error('Array should be higher than k');
}
highestProductArray = [arrayOfInts[0]];
lowestProductArray = [arrayOfInts[0]];
for (let i=1; i<k; i++) {
highestProductArray[i] = highestProductArray[i-1]*arrayOfInts[i];
lowestProductArray[i] = lowestProductArray[i-1]*arrayOfInts[i];
}
for(let i=1; i<arrayOfInts; i++) {
let currentInt = arrayOfInts[i];
for(let j=k-1; j>=0; j--) {
highestProductArray[j] = Math.max(
highestProductArray[j],
highestProductArray[j-1]*currentInt,
lowestProductArray[j-1]*currentInt
);
lowestProductArray[j] = Math.min(
lowestProductArray[j],
lowestProductArray[j-1]*currentInt,
highestProductArray[j-1]*currentInt
);
}
// highest number
highestProductArray[0] = Math.max(highestProductArray[0], currentInt)
// lowest number
lowestProductArray[0] = Math.max(lowestProductArray[0], currentInt)
}
return highestProductArray[k-1];
}
Any idea what I do wrong?
for the following example [1, 10, -5, 1, -100], the result is -50 instead of 5000.
lowest number is 1 and the highest is 1 instead of -100 and 10
The solution for the highestProduct of three of the integers:
var getHighestProductOfThree = function (arrayOfInts) {
if (arrayOfInts.length < 3) {
throw Error('Array should be higher than 3');
}
let highestProductOfThree = arrayOfInts[0]*arrayOfInts[1]*arrayOfInts[2];
let highestProductOfTwo = arrayOfInts[0]*arrayOfInts[1];
let lowestProductOfTwo = arrayOfInts[0]*arrayOfInts[1];
let highest = arrayOfInts[0];
let lowest = arrayOfInts[0];
for (let i=1; i<arrayOfInts.length; i++) {
let currentInt = arrayOfInts[i];
highestProductOfThree = Math.max(
highestProductOfThree,
highestProductOfTwo*currentInt,
lowestProductOfTwo*currentInt
);
highestProductOfTwo = Math.max(
highestProductOfTwo,
currentInt*highest,
currentInt*lowest
);
lowestProductOfTwo = Math.min(
lowestProductOfTwo,
currentInt*lowest,
currentInt*highest
);
highest = Math.max(
highest,
currentInt
);
lowest = Math.min(
lowest,
currentInt
);
}
return highestProductOfThree;
}
Here's an idea. Sort the numbers. Next, pick from the largest positive numbers as many as you can, up to k of them. Now pick the largest even group from the smallest negative numbers that form a larger product than the smallest positive numbers, which we will replace with them. (There are some edge cases, such as only one negative and k - 1 positives).
Pick 3 from [1, 10, -5, 1, -100]
Sort => [-100,-5,1,1,10]
Pick largest positives => 10 * 1 * 1
Pick largest even number of smallest negatives we can,
whose product is greater than the one replaced
=> (-100) * (-5) > 1 * 1
Answer => 10 * (-100) * (-5)
Based on my preliminary thoughts, I suggest to sort the values ascending, take the highest value, if the count is odd and use the rest with pairs.
This keeps a positive product with a loop until all needed factors are used.
In a while loop with a check for count, the pairs are chosen, if the product is greate than of the beginning of the array. This includes negative numbers, but works for only positive or negative numbers as well.
function getHighestProductOfK(a, k) {
var p = 1;
a.sort(function (a, b) { return a - b; });
if (k > a.length || k & 2 && a[a.length - 1] < 0) {
return;
}
if (k % 2) {
p = a.pop();
k--;
}
while (k) {
p *= a[0] * a[1] > a[a.length - 2] * a[a.length - 1] ? a.shift() * a.shift() : a.pop() * a.pop();
k -= 2;
}
return p;
}
console.log(getHighestProductOfK([1, 10, -5, 1, -100], 3));
console.log(getHighestProductOfK([3, 4, 5, 6, 7], 3));
console.log(getHighestProductOfK([-3, -4, -5, -6, -7], 3));
console.log(getHighestProductOfK([3, 4, -5, -6, -7], 3));
Needs some testing to make sure it always gives good answers..
function largestProduct(k, arr) {
if (k > arr.length) throw new RangeError('Not enough numbers');
let pos = [],
neg = [];
arr.forEach(e => {
if (e >= 0) pos.push(e);
else neg.push(e);
});
pos.sort((a, b) => a < b); // 9, 8, 7, ...
neg.sort((a, b) => a > b); // -9, -8, -7, ...
if (pos.length === 0 && k % 2) // k requires odd number of negatives
return neg.slice(-k); // give the smallest number TODO: same return
let big = [];
while (k > 1) grow();
if (k === 1) { // we've reached the end of doubles but still need more
if (pos.length) big.push(pos[0]);
else { // ran out of positives, backtrack
big = big.slice(0, -1);
big.push(neg[0], neg[1]);
}
}
return {
factors: big,
product: big.reduce((a, b) => a * b, 1)
};
function grow() { // choose the next best number
let doublepos = pos[0] * pos[1],
doubleneg = neg[0] * neg[1];
if (doublepos > doubleneg || doubleneg !== doubleneg) {
big.push(pos[0]);
pos = pos.slice(1);
k -= 1;
} else {
big.push(neg[0], neg[1]);
neg = neg.slice(2);
k -= 2;
}
}
}

Categories