Grouping an array elements in Javascript - javascript

How can I group the following array if the subtraction of elements is less than 2?
var myarr = [1.7, 2, 1.4, 6, 7, 14, 15, 21,31,33.2,33.5]
And I want to have this result:
var myarr = [[1.7, 2, 1.4], [6, 7], [14, 15], [21],[31,33.2,33.5]]

Reduce the array, and add a new sub array to the accumulator if the delta between the current and previous numbers is greater or equal to 2 (or it's the 1st number). Push the current number to the last array:
var myarr = [1.7, 2, 1.4, 6, 7, 14, 15, 21,31,33.2,33.5]
var result = myarr.reduce(function(r, n, i, arr) {
if(i === 0 || Math.abs(n - arr[i - 1]) >= 2) r.push([])
r[r.length - 1].push(n)
return r
}, []);
console.log(result)

I suggest to use the absolute value of the difference for checking with the wanted delta for inserting a new array in the result set, because the order of the values is not strictly ascending.
var array = [1.7, 2, 1.4, 6, 7, 14, 15, 21, 31, 33.2, 33.5],
delta = 2,
grouped = array.reduce(function (r, v, i, a) {
if (!i || Math.abs(a[i - 1] - v) > delta) {
r.push([]);
}
r[r.length -1].push(v);
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

How to find elements in array JavaScript where array[i] = i?

I need to find elements in an array of numbers where arr[i] === i, meaning the element must be equal to the array index.
They must be found with using recursion, not just by cycle.
I would be very thankful, if someone help, because I've spent many hours and can't do anything.
I've tried to use Binary Search but it doesn't work. In the end I've got only the empty array.
function fixedPointSearch(arr, low, high) {
let middle = Math.floor((high - low) / 2);
console.log( low, high, middle )
let arrRes = [];
if (arr[middle] === middle)
{ arrRes.push(arr[middle]); }
else if (arr[middle] > middle)
{ fixedPointSearch(arr, middle + 1, high); }
else
{ fixedPointSearch(arr, low, middle - 1); }
return arrRes;
}
const arr1 = [-10, -3, 2, 3, 6, 7, 8, 9, 10, 12, 16, 17];
console.log(fixedPointSearch(arr1, 0, arr1.length - 1));
To do this recursively, you presumably want to recurse on smaller and smaller arrays, but that means you need to also update the index you're checking on each call. One of the simplest ways to do this is just to include an index in the parameters to your function and increment it on each recursive call. This is one way to do so:
const fixedPointSearch = ([x, ...xs] = [], index = 0) =>
x == undefined
? []
: [... (x === index ? [x] : []), ... fixedPointSearch (xs, index + 1)]
console .log (
fixedPointSearch([-10, -3, 2, 3, 6, 7, 8, 9, 10, 12, 16, 17])
)
It's debatable whether that version or the following one is easier to read, but they are doing essentially the same thing:
const fixedPointSearch = ([x, ...xs] = [], index = 0) =>
x == undefined
? []
: x === index
? [x, ... fixedPointSearch (xs, index + 1)]
: // else
fixedPointSearch (xs, index + 1)
There is a potential problem, though. Running this over a large array, we could hit the recursion depth limit. If the function were tail-recursive, that problem would simply vanish when JS engines perform tail-call optimization. We don't know when that will be, of course, or even it it will actually ever happen, even though it's been specified for five years. But it sometimes makes sense to write to take advantage of it, on the hope that it will one day become a reality, especially since these will still work as well as the non-tail-call version.
So a tail-recursive version might look like this:
const fixedPointSearch = ([x, ...xs] = [], index = 0, res = []) =>
x == undefined
? res
: fixedPointSearch (xs, index + 1, x === index ? [...res, x] : res)
You can solve this w/o additional temporary arrays and parameters, by simply shortening the array in each step:
const myArray = [0, 5, 2, 4, 7, 9, 6];
function fixedPointSearch(arrayToTest) {
if (arrayToTest.length === 0) {
return [];
}
const lastIndex = arrayToTest.length - 1;
const lastItem = arrayToTest[lastIndex];
const remainingItems = arrayToTest.slice(0, lastIndex);
return lastItem === lastIndex
? [...fixedPointSearch(remainingItems), lastItem]
: fixedPointSearch(remainingItems);
}
console.log(fixedPointSearch(myArray));
If you want to find all the elements you should start from the beginning of the array, not the middle and loop through all the indexes.
The idea is for the recursion is to define the end condition.
Then you check if arr[i] === i to update the results array.
Then you make the recursive call with the index incremented and with the updated results array.
function fixedPointSearch(arr, i, results) {
// End condition of the recursion
if (i === arr.length - 1 || arr.length === 0) {
return results;
}
if (arr[i] === i) {
results.push(i);
}
// Recursive call
return fixedPointSearch(arr, i + 1, results);
}
const arr1 = [-10, -3, 2, 3, 6, 7, 8, 9, 10, 12, 16, 17];
console.log(fixedPointSearch(arr1, 0, []));
console.log(fixedPointSearch([], 0, []));
console.log(fixedPointSearch([9, 8, 7], 0, []));
The idiomatic solution in JavaScript uses Array.prototype.filter -
const run = (a = []) =>
a.filter((x, i) => x === i)
console.log(run([ 0, 1, 2, 3, 4, 5 ])) // [0,1,2,3,4,5]
console.log(run([ 3, 3, 3, 3, 3, 3 ])) // [3]
console.log(run([ 7, 1, 7, 3, 7, 5 ])) // [1,3,5]
console.log(run([ 9, 9, 9, 9, 9, 9 ])) // []
Above it should be clear that recursion isn't required for the job. But there's nothing stopping you from using it, if you wish -
const filter = (test = identity, a = [], i = 0) =>
{ /* base */
if (i >= a.length)
return []
/* inductive: i is in bounds */
if (test(a[i], i))
return [ a[i], ...filter(test, a, i + 1) ]
/* inductive: i is in bounds, a[i] does not pass test */
else
return filter(test, a, i + 1)
}
const run = (a = []) =>
filter((x, i) => x === i, a)
console.log(run([ 0, 1, 2, 3, 4, 5 ])) // [0,1,2,3,4,5]
console.log(run([ 3, 3, 3, 3, 3, 3 ])) // [3]
console.log(run([ 7, 1, 7, 3, 7, 5 ])) // [1,3,5]
console.log(run([ 9, 9, 9, 9, 9, 9 ])) // []
For recursion, you'll need an end condition. Something like
const findElementValueIsPositionInarray = arr => {
let results = [];
const find = i => {
if (arr.length) { // as long as arr has values
const value = arr.shift(); // get value
results = i === value // check it
? results.concat(value)
: results;
return find(i+1); // redo with incremented value of i
}
return results;
};
return find(0);
}
console.log(findElementValueIsPositionInarray([2,3,4,3,9,8]).join());
console.log(findElementValueIsPositionInarray([2,3,4,91,9,8]).join());
console.log(findElementValueIsPositionInarray([0,1,2,87,0,5]).join());
.as-console-wrapper { top: 0; max-height: 100% !important; }
I don't know why you want it through recursion:-
But anyway following should help you:-
let ans = [];
function find(arr,index,ans)
{
if(index==arr.length-1)
{
if(arr[index]==index){
ans.push(arr[index])
}
return;
}
if(arr[index]==index){
ans.push(arr[index])
}
find(arr,index+1,ans);
}
const arr1 = [-10, -3, 2, 3, 6, 7, 8, 9, 10, 12, 16, 17];
find(arr1,0,ans);
console.log(ans);

Sliding window over Array in JavaScript

I need a sliding window over an Array in JavaScript.
For example, a sliding window of size 3 over [1,2,3,4,5,6,7,8,9] shall compute the sequence [[1,2,3],[2,3,4],[3,4,5],[4,5,6],[5,6,7],[6,7,8],[7,8,9]].
The following is my attempt, because I couldn't find a readymade solution:
function window(a, sz) {
return a.map((_, i, ary) => ary.slice(i, i + sz)).slice(0, -sz + 1);
}
It returns an array of windows that can be mapped over to get the individual windows.
What is a better solution?
Array#reduce
A reasonable alternative to avoid .map followed by .slice() is to use .reduce() to generate the windows:
function toWindows(inputArray, size) {
return inputArray
.reduce((acc, _, index, arr) => {
if (index+size > arr.length) {
//we've reached the maximum number of windows, so don't add any more
return acc;
}
//add a new window of [currentItem, maxWindowSizeItem)
return acc.concat(
//wrap in extra array, otherwise .concat flattens it
[arr.slice(index, index+size)]
);
}, [])
}
const input = [1, 2, 3, 4, 5, 6, 7, 8, 9];
//JSON.stringify to produce more compact result in the console
console.log(JSON.stringify(toWindows(input, 2)));
console.log(JSON.stringify(toWindows(input, 3)));
console.log(JSON.stringify(toWindows(input, 4)));
console.log(JSON.stringify(toWindows(input, 9)));
console.log(JSON.stringify(toWindows(input, 10)));
//somewhat more realistic usage:
//find the maximimum odd sum when adding together three numbers at a time
const output = toWindows([ 3, 9, 1, 2, 5, 4, 7, 6, 8 ], 3)
.map(window => window.reduce((a,b) => a+b)) //sum
.filter(x => x%2 === 1) //get odd
.reduce((highest, current) => Math.max(highest, current), -Infinity) //find highest
console.log(output)
This can then be shortened, if needed:
function toWindows(inputArray, size) {
return inputArray
.reduce(
(acc, _, index, arr) => (index+size > arr.length) ? acc : acc.concat([arr.slice(index, index+size)]),
[]
)
}
const input = [1, 2, 3, 4, 5, 6, 7, 8, 9];
//JSON.stringify to produce more compact result in the console
console.log(JSON.stringify(toWindows(input, 2)));
console.log(JSON.stringify(toWindows(input, 3)));
console.log(JSON.stringify(toWindows(input, 4)));
console.log(JSON.stringify(toWindows(input, 9)));
console.log(JSON.stringify(toWindows(input, 10)));
//somewhat more realistic usage:
//find the maximimum odd sum when adding together three numbers at a time
const output = toWindows([ 3, 9, 1, 2, 5, 4, 7, 6, 8 ], 3)
.map(window => window.reduce((a,b) => a+b)) //sum
.filter(x => x%2 === 1) //get odd
.reduce((highest, current) => Math.max(highest, current), -Infinity) //find highest
console.log(output);
Array.from
The approach can be simplified using Array.from to generate an array with the appropriate length first and then populate it with the generated windows:
function toWindows(inputArray, size) {
return Array.from(
{length: inputArray.length - (size - 1)}, //get the appropriate length
(_, index) => inputArray.slice(index, index+size) //create the windows
)
}
const input = [1, 2, 3, 4, 5, 6, 7, 8, 9];
//JSON.stringify to produce more compact result in the console
console.log(JSON.stringify(toWindows(input, 2)));
console.log(JSON.stringify(toWindows(input, 3)));
console.log(JSON.stringify(toWindows(input, 4)));
console.log(JSON.stringify(toWindows(input, 9)));
console.log(JSON.stringify(toWindows(input, 10)));
//somewhat more realistic usage:
//find the maximimum odd sum when adding together three numbers at a time
const output = toWindows([ 3, 9, 1, 2, 5, 4, 7, 6, 8 ], 3)
.map(window => window.reduce((a,b) => a+b)) //sum
.filter(x => x%2 === 1) //get odd
.reduce((highest, current) => Math.max(highest, current), -Infinity) //find highest
console.log(output)
Generator
Another alternative is to use a generator function, instead of pre-computing all windows. This can be useful for more lazy evaluation with a sliding window approach. You can still compute all the windows using Array.from, if needed:
function* windowGenerator(inputArray, size) {
for(let index = 0; index+size <= inputArray.length; index++) {
yield inputArray.slice(index, index+size);
}
}
function toWindows(inputArray, size) {
//compute the entire sequence of windows into an array
return Array.from(windowGenerator(inputArray, size))
}
const input = [1, 2, 3, 4, 5, 6, 7, 8, 9];
//JSON.stringify to produce more compact result in the console
console.log(JSON.stringify(toWindows(input, 2)));
console.log(JSON.stringify(toWindows(input, 3)));
console.log(JSON.stringify(toWindows(input, 4)));
console.log(JSON.stringify(toWindows(input, 9)));
console.log(JSON.stringify(toWindows(input, 10)));
//somewhat more realistic usage:
//find the sum closest to a target number when adding three numbers at a time
const veryLargeInput = [17, 95, 27, 30, 32, 38, 37, 67, 53, 46, 33, 36, 79, 14, 19, 25, 3, 54, 98, 11, 68, 96, 89, 71, 34, 31, 28, 13, 99, 10, 15, 84, 48, 29, 74, 78, 8, 90, 50, 49, 59, 18, 12, 40, 22, 80, 42, 21, 73, 43, 70, 100, 1, 44, 56, 5, 6, 75, 51, 64, 58, 85, 91, 83, 24, 20, 72, 26, 88, 66, 77, 60, 81, 35, 69, 93, 86, 4, 92, 9, 39, 76, 41, 37, 63, 45, 61, 97, 2, 16, 57, 65, 87, 94, 52, 82, 62, 55, 7, 23];
const targetNumber = 100;
console.log(`-- finding the closest number to ${targetNumber}`)
const iterator = windowGenerator(veryLargeInput, 3);
let closest = -1;
for (const win of iterator) {
const sum = win.reduce((a, b) => a+b);
const difference = Math.abs(targetNumber - sum);
const oldDifference = Math.abs(targetNumber - closest);
console.log(
`--- evaluating: ${JSON.stringify(win)}
sum: ${sum},
difference with ${targetNumber}: ${difference}`
);
if (difference < oldDifference) {
console.log(`---- ${sum} is currently the closest`);
closest = sum;
if (difference === 0) {
console.log("----- prematurely stopping - we've found the closest number")
break;
}
}
}
console.log(`-- closest sum is: ${closest}`)
Have you considered going recursive?
l is the size of each window
xs is your list
i is the number of iterations we need to make which is xs.length - l
out contains the result
A slice can be obtained with xs.slice(i, i + l). At each recursion i is incremented until i gets to a point where the next slice would contain less than l elements.
const windows = (l, xs, i = 0, out = []) =>
i > xs.length - l
? out
: windows(l, xs, i + 1, [...out, xs.slice(i, i + l)]);
console.log(windows(3, [1,2,3,4,5,6,7,8,9]));
There is also a non-recursive solution with flatMap.
With flatMap you can return an array at each iteration, it will be flattened in the end result:
const duplicate = xs => xs.flatMap(x => [x, x]);
duplicate([1, 2]);
//=> [1, 1, 2, 2]
So you can return your slices (wrapped in []) until i gets over the limit which is xs.length - l:
const windows = (l, xs) =>
xs.flatMap((_, i) =>
i <= xs.length - l
? [xs.slice(i, i + l)]
: []);
console.log(windows(3, [1,2,3,4,5,6,7,8,9]))
Note that in some libraries like ramda.js, this is called aperture:
Returns a new list, composed of n-tuples of consecutive elements. If n is greater than the length of the list, an empty list is returned.
aperture(3, [1,2,3,4,5,6,7,8,9]);
//=> [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9]]
As you can see a few people had the same question before:
how I can solve aperture function in javascript?
How to create windowed slice of array in javascript?
Adding to the native JavaScript objects through their prototype is not a good idea. This can break things in unexpected ways and will cause a lot of frustration for you and anyone else using your code. It is better to just create your own function in this case.
To get the functionality you want, you could simply pass the array to your function and then access it from there. Make the method calls you want on the array from your function. Following the principle of KISS, there's no need for anything more fancy here.
Also, remember that Array.map is called for each element of the array. That's not really what you need here. If the goal is to get a sliding window of size n, and you want each of the windows to be added to a new array, you could use a function like this:
const myArray = [1, 2, 3, 4, 5, 6, 7, 8];
const slicingWindows = (arr, size) => {
if (size > arr.length) {
return arr;
}
let result = [];
let lastWindow = arr.length - size;
for (let i = 0; i <= lastWindow; i += 1) {
result.push(arr.slice(i, i + size));
}
return result;
};
So here, we will get an array of windows, which are also arrays. Calling console.log(slicingWindows(a,3)), gives this output:
[1, 2, 3]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
Using JS ES6, you can do the following:
class SlidingWindow{
constructor(windowSize) {
this.deque = []; // for storing the indexex of the 'k' elements in the input
this.windowSize = windowSize;
}
compute(input){
let output = [];
if(!input || input.length === 0) {
return [];
}
if(input.length < this.windowSize) {
return input;
}
for(let i=0; i < input.length; i++) {
//if the index in the first element of the this.deque is out of bound (i.e. idx <= i-this.windowSize) then remove it
if(this.deque.length > 0 && this.deque[0] === i-this.windowSize) {
this.deque.shift(); //remove the first element
}
this.deque.push(i)
if(i+1 >= this.windowSize) {
output.push(this.deque.map(idx => input[idx]));
}
}
return output;
}
}
//Here is how to use it:
let slidingWindow = new SlidingWindow(3);
console.log('computed sliding windows: '+JSON.stringify(slidingWindow.compute([1,2,3,4,5,6,7,8,9])));
To compute the maximum of each sliding window, you can customise the above code as follows:
class SlidingWindow{
constructor(windowSize) {
this.deque = []; // for storing the indexex of the 'k' elements in the input
this.windowSize = windowSize;
}
customCompute(input, processWindow, addOutput) {
let output = [];
if(!input || input.length === 0) {
return [];
}
if(input.length < this.windowSize) {
return input;
}
for(let i=0; i < input.length; i++) {
//if the index in the first element of the this.deque is out of bound (i.e. idx <= i-this.windowSize) then remove it
if(this.deque.length > 0 && this.deque[0] === i-this.windowSize) {
this.deque.shift(); //remove the first element
}
processWindow(this.deque, input[i], input)
this.deque.push(i)
if(i+1 >= this.windowSize) {
output.push(addOutput(this.deque, input));
}
}
this.deque = [];
return output;
}
}
let slidingWindow = new SlidingWindow(3);
console.log('computed sliding windows: '+JSON.stringify(slidingWindow.compute([1,2,3,4,5,6,7,8,9])));
function processWindow(deque, currentElement, input){
while(deque.length > 0 && currentElement > input[deque[deque.length -1]]) {
deque.pop(); //remove the last element
}
};
console.log('computed sliding windows maximum: '+JSON.stringify(slidingWindow.customCompute([1,3,-1,-3,5,3,6,7], processWindow, (deque, input) => input[deque[0]])));
Simple while-loop solution
function windowArray(array, windowSize) {
return array.map((value, index) => {
const windowedArray = [];
while (array[index] && windowedArray.length < windowSize) {
windowedArray.push(array[index]);
index++;
}
return windowedArray;
});
};
const array = [1, 1, 1, 2, 2, 2, 3, 3, 3]
const windowValue = 3;
const windowedArray = windowArray(array, windowValue)
const filteredWindowedArray = windowedArray.filter(group => group.length === windowValue);
console.log("INPUT ARRAY", JSON.stringify(array))
console.log("WINDOWED ARRAY", JSON.stringify(windowedArray));
console.log("FILTERED WINDOWED ARRAY", JSON.stringify(filteredWindowedArray));

differntiate same values in array, js

What i want to achieve is to make every element in array different and unique from others.
So if you imagine such a array:
let myArray = Array(10, 10, 10, 10, 9);
I want to put it through such algorithm:
Takie first element of an array (10) (index = 0).
check if there is element in array with the same value. yes there is - with index 1. if not, check next element...
change myArray[0] to myArray[0] + 1
change myArray[1] to myArray[1] - 1
return myArray
Now array looks like this:
myArray = Array(11, 9, 10, 10, 9)
Now it runs again, and next steps return such array:
myArray = Array(11, 10, 10, 10, 8)
myArray = Array(11, 11, 9, 10, 8)
myArray = Array(12, 10, 9, 10, 8)
myArray = Array(12, 11, 9, 8, 8)
myArray = Array(12, 11, 10, 9, 7)
Now, because there are only unique values, it finishes.
It is easy to make it with while and for loops, but how can i do it with ES6 functional programming?
K.
You could use a nested iteration with short circuit, if two same values are found.
function fn(array) {
return array.some((v, i, a) => a.slice(i + 1).some((w, j) => {
if (v === w) {
++a[i];
--a[i + j + 1];
return true;
}
}));
}
var array = [10, 10, 10, 10, 9];
console.log(array.join(' '));
while (fn(array)) console.log(array.join(' '));
.as-console-wrapper { max-height: 100% !important; top: 0; }
A more functional style, which returns a new array
const
init = j => (v, i, a) => j !== -1
? i === j ? v - 1 : v
: (j = a.indexOf(v, i + 1)) !== -1 ? v + 1 : v,
upDown = () => init(-1);
var array = [10, 10, 10, 10, 9];
do {
console.log(array.join(' '))
} while (array.join() !== (array = array.map(upDown())).join())
.as-console-wrapper { max-height: 100% !important; top: 0; }

Array truncation with splice method

I need to delete occurrences of an element if it occurs more than n times.
For example, there is this array:
[20,37,20,21]
And the output should be:
[20,37,21]
I thought one way of solving this could be with the splice method
First I sort the array it order to make it like this:
[20,20,37,21]
Then I check if the current element is not equal to the next and split the array into chunks, so it should look like:
[20, 20],[37],[21]
Later I can edit the chunk longer than 1 and join it all again.
This is what the code looks like in my head but didn't work in real life
var array = [20, 37, 20, 21];
var chunk = [];
for(i = 0; i < array.length; i++) {
if(array[i] !== array[i + 1]) {
var index = array.indexOf(array[i]);
chunk.push = array.splice(0, index) // cut from zero to last duplicate element
} else
var index2 = a.indexOf(a[i]);
chunk.push(a.splice(0, index));
}
with this code the output is
[[], [20, 20]]
I think It's something in the 'else' but can't figure it out what to fix.
As the logic you want to achieve is to delete n occurrences of element in an array, your code could be as follow:
var array = [1, 1, 3, 3, 7, 2, 2, 2, 2];
var n = 2;
var removeMultipleOccurences = function(array, n) {
var filteredArray = [];
var counts = {};
for(var i = 0; i < array.length; i++) {
var x = array[i];
counts[x] = counts[x] ? counts[x] + 1 : 1;
if (counts[x] <= n) filteredArray.push(array[i])
}
return filteredArray;
}
console.log(removeMultipleOccurences(array, n));
I came up with this one, based on array filter checking repeated values up to a limit, but I can see #Basim's function does the same.
function removeDuplicatesAbove(arr, max) {
if (max > arr.length) {max = arr.length;}
if (!max) {return arr;}
return arr.filter(function (v, i) {
var under = true, base = -1;
for (var n = 0; n < max; n++) {
base = arr.indexOf(v, base+1); if (base == -1) {break;}
}
if (base != -1 && base < i) {under = false;}
return under;
});
}
var exampleArray = [20, 37, 20, 20, 20, 37, 22, 37, 20, 21, 37];
console.log(removeDuplicatesAbove(exampleArray, 3)); // [20, 37, 20, 20, 37, 22, 37, 21]
Always when you use splice() you truncate the array. Truncate the array with the length of same values from the start with the help of lastIndexOf(). It always starts from 0.
[ 1, 1, 1, 2, 2, 2, 3, 4, 4, 5 ] // splice(0, 3)
[ 2, 2, 2, 3, 4, 4, 5 ] // splice(0, 3)
[ 3, 4, 4, 5 ] // splice(0, 1)
[ 4, 4, 5 ] // splice(0, 2)
[ 5 ] // splice(0, 1)
Do this as long as the array length is greater than 0.
var arr = [1, 1, 1, 2, 2, 2, 3, 4, 4, 5];
var res = [];
while (arr.length > 0) {
var n = arr[0];
var last = arr.lastIndexOf(n) + 1;
res.push(n);
arr.splice(0, last);
}
console.log(res);
You can use Array.prototype.reduce(), Array.prototype.filter() to check if n previous elements are the same as current element
let cull = (arr, n) => arr.reduce((res, curr) => [...res
, res.filter(v => v === curr).length === n
? !1 : curr].filter(Boolean), []);
let arrays = [[20,37,20,21], [1,1,3,3,7,2,2,2,2]];
let cullone = cull(arrays[0], 1);
let cullthree = cull(arrays[1], 3);
console.log(cullone // [20, 37, 21]
, cullthree // [1, 1, 3, 3, 7, 2, 2, 2]
);

Use javascript to sort an array of ints by the distance the element is from a given target

Algorithm: distanceSort(array, target)
Input: An array of ints sorted from least to greatest and an int to measure distance from
Output: The array sorted by distance from target
Example
distanceSort([-10,-6,3,5], 1)
returns [3, 5, -6, -10]
Here's a way to do it. Using Array.sort function
var a = [-10, -6, 3, 5, 99, 76, -100];
function distanceSort(arr, target) {
return arr.sort(function(a, b) {
var distance1 = Math.abs(target - a);
var distance2 = Math.abs(target - b);
return distance1 == distance2 ? 0 : (distance1 > distance2 ? 1 : -1);
});
}
console.log(distanceSort(a, 100)); //[99,76,5,3,-6, -10, -100]
console.log(distance(a, -5)); //[-6, -10, 3, 5, 76, -100, 99]
First, perform a binary search (I always copy from here) to find the target inside the array (if it exists, or the immediatelly greater otherwise).
Then, keep two pointers moving in oposing directions adding always the element with less distance from the target.
The binary search is O(log n) and moving the pointers is O(n). The overall algorithm is O(n).
function lowerBound(arr, target) {
var first = 0,
count = arr.length;
while (count > 0) {
var step = count / 2;
var it = first + step;
if (arr[it] < target) {
first = it + 1;
count -= step + 1;
} else {
count = step;
}
}
return first;
}
function distanceSort(arr, target) {
var answer = [];
var j = lowerBound(arr, target);
var i = j-1;
while (i >= 0 || j<arr.length) {
if (j >= arr.length || target-arr[i]<arr[j]-target)
answer.push(arr[i--]);
else
answer.push(arr[j++]);
}
return answer;
}
console.log(distanceSort([-10,-6,3,5], 1)); //[3, 5, -6, -10]
console.log(distanceSort([-10,-6,3,5], -11)); //[-10, -6, 3, 5]
console.log(distanceSort([-10,-6,3,5], -10)); //[-10, -6, 3, 5]
console.log(distanceSort([-10,-6,3,5], 5)); //[5, 3, -6, -10]
console.log(distanceSort([-10,-6,3,5], 6)); //[5, 3, -6, -10]
var distanceSort = function distanceSort(nArr, x){
return nArr.sort(function(n1, n2){
return Math.abs(n1 - x) - Math.abs(n2 - x);
});
}
console.log(distanceSort([-10,-6, 57, 54, 11, -34, 203, -140, 3, 5], 1));
//--> [3, 5, -6, 11, -10, -34, 54, 57, -140, 203]
It translates nicely into ECMA6 (if you're using Babel) :
var distanceSort = (nArr, x) =>
(nArr.sort((n1, n2) =>
(Math.abs(n1 - x) - Math.abs(n2 - x))));

Categories