Sorting an array by their ascending value within the array in JS - javascript

It's a bit of a tricky situation I'm in, but I have an array like this:
const nums = [32, -3, 62, 8, 121, -231, 62, 13];
and need to replace them by their corresponding ascending index. The above example should yield:
[4, 1, 5, 2, 7, 0, 6, 3]
The solution I've come up with is this: TS Playground
const nums = [32, -3, 62, 8, 121, -231, 62, 13];
const numsCopy = nums.map(e => e);
// Basic sorting
for (let i = 0; i < numsCopy.length; i++) {
for (let j = 0; j < numsCopy.length; j++) {
if (numsCopy[i] < numsCopy[j]) {
let t = numsCopy[j];
numsCopy[j] = numsCopy[i];
numsCopy[i] = t;
}
}
}
for (let i = 0; i < numsCopy.length; i++) {
let sortedValue = numsCopy[i];
nums[nums.indexOf(sortedValue)] = i;
}
Problems arise however when I change nums to include a value nums.length > n >= 0. The call nums.indexOf(...) may return a faulty result, as it may have already sorted an index, even though it exists somewhere in the array.
If you replace nums with these values, -231 will have an index of 2 for some reason...
const nums = [32, -3, 62, 7, 121, -231, 62, 13, 0];
> [5, 1, 6, 3, 8, 2, 7, 4, 0]
Is there a better approach to this problem, or a fix to my solution?

You could sort the indices by the value and create a new array with index values a sorted positions.
to get the wanted result call the sorting function again and you get the indices sorted by the index order.
const
sort = array => [...array.keys()].sort((a, b) => array[a] - array[b]),
fn = array => sort(sort(array));
console.log(...fn([32, -3, 62, 8, 121, -231, 62, 13])); // 4 1 5 2 7 0 6 3
console.log(...fn([-1, 3, 1, 0, 2, 9, -2, 7])); // 1 5 3 2 4 7 0 6

Copy the array, sort its values, get indexOf, and null the value in the sorted copy:
const sortIndicesByValue = array => {
const sorted = [...array].sort((a, b) => a - b);
return array.map(e => {
const i = sorted.indexOf(e);
sorted[i] = null;
return i;
})
}
console.log(...sortIndicesByValue([32, -3, 62, 8, 121, -231, 62, 13]));
console.log(...sortIndicesByValue([-1, 3, 0, 0, 2, 9, -2, 7]));

Related

How to find three largest numbers in an array?

Hello I want to find the three largest numbers in an array by ORDER.
I am confused how I should implement the last logic where I have to shift the indexes of the result array based on if the the current number in the result array is greater or less than the loop of the array items.
function findThreeLargestNumbers(array) {
let result = [null, null, null];
for (let i = 0; i < array.length; i++) {
if (!result[2] || result[i] > result[2]) {
for (let j = 0; i <= 2; i++) {
if (j === 2) {
result[j] = array[i]
} else {
result[j] = array[i + 1]
}
}
}
}
return result
}
console.log(findThreeLargestNumbers([141, 1, 17, -7, -17, -27, 18, 541, 8, 7, 7]));
You can simply sort it in ascending order and use slice to get the last 3 elements as:
1) When you want result in last 3 largest in ascending order [18, 141, 541]
function findThreeLargestNumbers(array) {
return [...array].sort((a, b) => a - b).slice(-3);
}
// [18, 141, 541]
console.log(findThreeLargestNumbers([141, 1, 17, -7, -17, -27, 18, 541, 8, 7, 7]));
2) When you want result in last 3 largest in descending order [541, 141, 18]
function findThreeLargestNumbers(array) {
return [...array].sort((a, b) => b - a).slice(0, 3);
}
console.log(findThreeLargestNumbers([141, 1, 17, -7, -17, -27, 18, 541, 8, 7, 7]));

How to replace the value of element which is not found in another array in ascending order?

let newer = [7,8,10,0,2,3,9,24,1,4,20,19,23,5,21,6,22];
let indices_2= [3,5,0,1,4,6,7,10,**13**,**16**,8,2,9,**14**,**15**,**11**,**12**];
Output should be = [3,5,0,1,4,6,7,10,**21**,**24**,8,2,9,**22**,**23**,**19**,**20**];
Hi guys, this might be tough. If the element in indices_2 is not found in newer, from smallest to largest value, lets say the first smallest number of indices_2 not found in newer is 11, it is replaced by the first smallest number found in newer which is not found in indices_2 which is 19. Then the sequence continues on for the the second smallest number of indices_2 not found in newer.
let newer=[7,8,10,0,2,3,9,24,1,4,20,19,23,5,21,6,22];
let indices_2=[3,5,0,1,4,6,7,10,13,16,8,2,9,14,15,11,12];
let status_indices=[]; let status_indices_stat=[];
for (let i=0;i<newer.length;i++){
status_indices_stat="f"
for (let f=0;f<newer.length;f++){
if (indices_2[i]==newer[f]){
status_indices_stat="t"
//check whether element is found in newer.
}
}
status_indices.push(status_indices_stat)
}
for (let f=0;f<newer.length;f++){
if (status_indices[f]=="f"){
for (let i=0;i<newer.length;i++){
if (indices_2[f]<newer[i]){
console.log(i)
}
}
}
}
You could filter both arrays with the opposite, sort them and take the array with filtered indices as pattern for the index of getting the value of the other filtered and sorted array.
let newer = [7, 8, 10, 0, 2, 3, 9, 24, 1, 4, 20, 19, 23, 5, 21, 6, 22],
indices2 = [3, 5, 0, 1, 4, 6, 7, 10, 13, 16, 8, 2, 9, 14, 15, 11, 12],
temp1 = newer.filter(v => !indices2.includes(v)).sort((a, b) => a - b),
temp2 = indices2.filter(v => !newer.includes(v)).sort((a, b) => a - b),
result = indices2.map(v => newer.includes(v) ? v : temp1[temp2.indexOf(v)]);
console.log(...result);

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));

Create a New Index Array

Not sure why this isn't working.
Instructions:
// Create a function called indexFinder that will loop over an array and return a new array of the indexes of the contents e.g. [243, 123, 4, 12] would return [0,1,2,3]. Create a new variable called 'indexes' and set it to contain the indexes of randomNumbers.
Tried Solution:
let randomNumbers = [1, 3453, 34, 456, 32, 3, 2, 0];
let indexes = [];
function indexFinder(arr){
for(var i = 0; arr.length; i++){
indexes.push(i);
}
return indexes;
}
indexFinder(randomNumbers);
console.log(indexes);
You have no real condition test in your for loop because arr.length, when above 0, is always truthy.
let randomNumbers = [1, 3453, 34, 456, 32, 3, 2, 0];
let indexes = [];
function indexFinder(arr){
for(var i = 0; i < arr.length; i++){
indexes.push(i);
}
return indexes;
}
indexFinder(randomNumbers);
console.log(indexes);
But there's a much more concise way of doing this:
const randomNumbers = [1, 3453, 34, 456, 32, 3, 2, 0];
const indexFinder = arr => arr.map((_, i) => i);
console.log(indexFinder(randomNumbers));
Another method is using Array.from
const randomNumbers = [1, 3453, 34, 456, 32, 3, 2, 0];
console.log(Array.from(randomNumbers, x => randomNumbers.indexOf(x)));
Or we can use keys
const randomNumbers = [1, 3453, 34, 456, 32, 3, 2, 0];
console.log([...Array(randomNumbers.length).keys()])
The problem is the condition within that for-loop using just arr.length because for length greater than 0 will be always true.
An alternative is using the function Array.from:
let randomNumbers = [1, 3453, 34, 456, 32, 3, 2, 0],
indexes = Array.from({length: randomNumbers.length}, (_, i) => i);
console.log(indexes);
Another alternative is getting the length and then execute a simple for-loop.

Common values in multiple variable lenght javascript arrays

I have 7 arrays in javascript and I need to find values that are present in all of them.
I don't think I'm the first one to ask this but I can't find a solution for this. I read many answers but they all compare only 2 arrays and that logic don't work for multiple arrays.
I tried functions proposed in Simplest code for array intersection in javascript but they don't fit the kind of arrays I have.
The arrays I have can have different lengths in elements and the element's lengtt can vary too. I also may have zero item arrays in which they should not be compared against.
The main problem is with different number lengths. All functions I tried require sorting but this causes a problem.
Given this arrays:
xnombre = [1,2,3,4,5,24,44,124,125,165];
xacomp = [1,5,44,55,124];
xeje = [];
xanio = [1,5,44,55,124];
xini = [1,5,44,55,124];
xaporte = [1,5,44,55,122,123,124,144,155,166,245];
xpcia = [2,1,3,4,6,5,7,9,12,12,14,15,44,16,17,19,124];
The first to arrays are sorted to:
[1, 124, 125, 165, 2, 24, 3, 4, 44, 5]
[1, 124, 44, 5, 55]
Which when I "intersect" I only get [1,124] but 44 and 5 are missed.
Any help would be appreciated.
Thanks
The function from the other question works, but you have to sort your array numerically, not lexicographically, since you are working with numbers, not strings.
function sortNumber(a,b) {
return a - b;
}
var xnombre = [1,2,3,4,5,24,44,124,125,165];
var xacomp = [1,5,44,55,124];
xnombre.sort(sortNumber);
xacomp.sort(sortNumber);
DEMO
To apply this to multiple arrays, you could apply this function consecutively:
// var result = intersect(a, b, c, ...);
function intersect(var_args) {
// sort arrays here or beforehand
var target = arguments[0];
for (var i = 1; i < arguments.length; i++) {
if (arguments[i].length > 0) {
target = intersection_safe(target, arguments[i]);
}
}
return target;
}
This requires some of the new array methods, but it produces your desired output.
function intersection() {
var arrs = Array.prototype.filter.call(arguments, function (a) {
return a.length > 0;
}).sort(function (a, b) { // sort the arrays, so that we test the shortest.
return a.length - b.length;
});
var rest = arrs.slice(1),
test = arrs[0];
return test.filter(function (x) { return rest.every(function (a) { return a.indexOf(x) !== -1; }); });
}
var xnombre = [1, 2, 3, 4, 5, 24, 44, 124, 125, 165],
xacomp = [1, 5, 44, 55, 124],
xeje = [],
xanio = [1, 5, 44, 55, 124],
xini = [1, 5, 44, 55, 124],
xaporte = [1, 5, 44, 55, 122, 123, 124, 144, 155, 166, 245],
xpcia = [2, 1, 3, 4, 6, 5, 7, 9, 12, 12, 14, 15, 44, 16, 17, 19, 124];
intersection(xnombre, xacomp, xeje, xanio, xini, xaporte, xpcia)
// => [1, 5, 44, 124]
I tried your problem with underscore.
var _ = require('underscore');
xnombre = [1,2,3,4,5,24,44,124,125,165];
xacomp = [1,5,44,55,124];
xeje = [];
xanio = [1,5,44,55,124];
xini = [1,5,44,55,124];
xaporte = [1,5,44,55,122,123,124,144,155,166,245];
xpcia = [2,1,3,4,6,5,7,9,12,12,14,15,44,16,17,19,124];
var result = _.intersection(xnombre,xacomp,xanio,xini,xaporte,xpcia);
console.log(result);
But as you see that I haven't given the empty array, so somehow you have to ignore empty array.
Fiddle

Categories