How to sort 2 dimensional array (matrix) in JavaScript? - javascript

I want to sort a 2 dimensional array of integers. What is the simplest and most readable way to achieve this?
input:
[
[3,4,2],
[5,1,3],
[2,6,1],
]
output:
[
[1,1,2],
[2,3,3],
[4,5,6]
]

If you'd need the deeper arrays to be in order as well, I'd tackel it like so:
Flatten the arrays using flat(), so get just a regular list
input.flat()
Sort them using a custom integer sort function
.sort((a, b) => a - b)
Re-create the second dimension
array_chunks(sortedArray, 3);
(Function used taken from this answer)
const input = [
[3,4,2],
[5,1,3],
[2,6,1],
];
const array_chunks = (array, chunk_size) => Array(Math.ceil(array.length / chunk_size)).fill().map((_, index) => index * chunk_size).map(begin => array.slice(begin, begin + chunk_size));
let result = array_chunks(input.flat().sort((a, b) => a - b), 3);
console.log(result);
[
[ 1, 1, 2 ],
[ 2, 3, 3 ],
[ 4, 5, 6 ]
]

When working with nested arrays, its common to work from the inner most level and then step out. Using this method, we start with the inner arrays:
// Sorting the inner most array
var result = [];
[
[3,4,2],
[5,1,3],
[2,6,1],
].forEach(function(row) { // a for loop could work as well, this is just shorter.
result.push(row.sort(function(a, b) {
return a-b; // ascending
// return b-a; // descending
}));
});
Then, you sort the outer array. You cannot sort the outer array as a single number, so its necessary to decide on a method. Examples being: average of the array, lowest value, or highest value.
// Using the first value to sort the outer array.
[
[2,3,4],
[1,3,5],
[1,2,6],
].sort(function(a, b) {
return a[0]-b[0];
});
Alternatively
The wanted output "[[1,1,2],[2,3,3],[4,5,6]]" disregards the inner arrays so it doesn't seem practical. To do this however, we'd reconstruct all inner arrays into one, sort, and then rebuild a nested array assuming each new array is to have 3 values.
var oneDimensionalArray = [];
var finalArray = [];
[
[3,4,2],
[5,1,3],
[2,6,1],
].forEach(function(row) {
oneDimensionalArray = oneDimensionalArray.concat(row);
});
oneDimensionalArray.sort();
for (var i=0; i<oneDimensionalArray.length; i++) {
if (i%3==0) {
temp = [];
}
temp.push(oneDimensionalArray[i]);
if (i%3==2) { // 3 per line (0-2)
finalArray.push(temp);
}
}
Again, I don't see this having practical usefulness. It would be easier to leave them as a regular array or start with a different setup if all the data is to be used in a way that disregards grouping/array.

This is a general approach of nested array, which could have more nested arrays or different lengths.
It works by taking an array of indices to every value, a flat array of values and finally by assigning all values back to their place.
const
getIndices = (value, index) => Array.isArray(value)
? value.flatMap(getIndices).map(array => [index, ...array])
: [[index]],
setValue = (array, keys, value) => {
const last = keys.pop();
keys.reduce((a, i) => a[i] ??= [], array)[last] = value;
return array;
};
data = [[3, 4, 2], [5, 1, 3], [2, 6, 1]],
references = data.flatMap(getIndices),
result = data
.flat()
.sort((a, b) => a - b)
.reduce((r, v, i) => setValue(r, references[i], v), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Node - Merge dynamic amount of arrays together - alternate the items in the result array

I have a bunch of arrays of different length holding objects of the same types. What I want is to merge all these arrays into one final array. The issue I have is that the items need to alternate in the final result array.
so if for example one array only holds one value to altenate the results until there is no element in the shortest length array and continue with just the others still holding values.
const arr1 = [arr1Obj, arr1Obj, arr1Obj]
const arr2 = [arr2Obj, arr2Obj, arr2Obj]
const arr3 = [arr3obj]
//no pre-defined amount of arrays, could be only one array or 10+
//output desired
[arr1Obj, arr2Obj, arr3Obj, arr1Obj, arr2Obj, arr1Obj, arr2Obj]
My approach was just using a bunch of nested for-loops which I am sure is not the best way of doing this. I am wondering, how would the shortest most performant es6 way look alike? Preferably without using modules like lodash.
We can use Array.from() and Array.reduce() to get the sequence we wish for.
We start by getting the max array length using Math.max(), then we use Array.from() to create our output array, using Array.reduce() to append each element as we iterate over the arrays, flattening at the end:
const arr1 = ['arr1Obj', 'arr1Obj', 'arr1Obj']
const arr2 = ['arr2Obj', 'arr2Obj', 'arr2Obj']
const arr3 = ['arr3Obj']
// Sort our input by length, we want the longest array first...
const arrays = [arr1, arr2, arr3];
const maxLength = Math.max(...arrays.map(a => a.length));
const result = Array.from({ length: maxLength }, (v, idx) => arrays.reduce((acc, arr) => {
if (idx < arr.length) {
acc.push(arr[idx]);
}
return acc;
}, [])).flat();
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use .reduce to n arrays with the jth elements and then .push all of their elements:
let arrs = [
[1,2,3],
[2,undefined,'4',6,90,3.14],
[1,0,-1,5],
[],
[100, 300, -Infinity]
];
let merged = [];
for (var i = 0, j = 0; i < arrs.length; i++, j++) {
merged.push(...arrs.reduce((a, c) => {
if (c.length > j) a.push(c[j]);
return a;
}, []));
}

In Javascript how do I create a secondary array in a specific index?

How do I create a subarray from an existing array in Javascript?
For example;
Arr = [5,2,1,2]
Then I want to insert 8 in position 1 of Arr, but keep the original value 2. So arr can become:
Arr = [5,[2,8],1,2]
I tried using concat, which sort of did something but duplicates all values.
Bear in mind that this can grow e.g. Arr = [5,[2,8,3,4],1,[2,3]]
Thanks!
You could assign the concatinated values.
const
addAt = (array, value, index) => array[index] = [].concat(array[index], value),
array = [5, 2, 1, 2];
addAt(array, 8, 1);
console.log(array)
addAt(array, 3, 1);
console.log(array)
.as-console-wrapper { max-height: 100% !important; top: 0; }
There are several different ways to do this, but you can reassign the current array index to an array like so:
Arr[1] = [Arr[1], 8]. Then if you wanted to continue adding to the array at index 1 in Arr, you could do something like Arr[1].push(x).
You could do something like this (Probably not the best answer, but may be helpful)
const addIntoArray = (arr, toAdd, index) => {
arr[index] = typeof arr[index] == "number" ? [arr[index], toAdd] : [...arr[index], toAdd];
return arr
}
Arr = [5,2,1,2]
console.log(Arr); // [ 5, 2, 1, 2 ]
addIntoArray(Arr, 1, 1)
console.log(Arr); // [ 5, [ 2, 1 ], 1, 2 ]
addIntoArray(Arr, 3, 1)
console.log(Arr) // [ 5, [ 2, 1, 3 ], 1, 2 ]
Basically ...arr expands the array and then we add toAdd at it's end and [arr[index], toAdd] creates an array with the first element as the number and the second the new element. (It modifies the original array, as shown in the example, so pay attention as it may lead to bugs)
The typeof arr[index] == "number"is just a simple/generic typecheck to see if there isn't an array already
This function should satisfy your conditions at basic level
// a - array, v - value to insert, i - position to insert
const avi = (a, v, i) => {
r = a.slice(0, i);
r.push([a[i], v]);
a.slice(i+1, a.length).forEach(e => r.push(e));
return r;
}
console.log(JSON.stringify(avi([5,2,1,2], 8, 1)))
//=> "[5,[2,8],1,2]"

I have an array of numbers. I need to return a new array in which the duplicate numbers are in their own nested array

I need to take this array [1,2,4,591,392,391,2,5,10,2,1,1,1,20,20] and return a new array which sorts all the duplicate numbers into their own nested array.
it should return [[1,1,1,1],[2,2,2], 4,5,10,[20,20], 391, 392,591].
I have messed around with for loops as well as the .map() method and can't seem to land on the solution. At this point I am almost just taking shots in the dark hoping something will work out. I am very new at javascript.
const cleaner = array1.map((num, index, array) => {
if (array1.indexOf(num) >= index) {
return num;
} else {
return array;
}
});
You can use a Set to collect the unique values then use Array.prototype.map() and Array.prototype.filter() to create the subarrays for each value.
const array = [1,2,4,591,392,391,2,5,10,2,1,1,1,20,20];
// defines a function that...
const arrange = array =>
// creates array of unique values
Array.from(new Set(array))
// sorts unique values in ascending order
.sort((a, b) => a - b)
// creates a subarray for each unique value
.map(key => array.filter(value => key === value))
// unwraps unit length subarrays
.map(subarray => subarray.length === 1 ? subarray[0] : subarray);
const cleaner = arrange(array);
console.log(JSON.stringify(cleaner));
This is not the most efficient approach, but it is considerably more readable (in my opinion) than procedural approaches using the reduce() method, and for an array of this length, the difference in execution time will be negligible.
You can do this using reduce, see the comments in the snippet :
const arr = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
const result = arr.reduce((acc, curr) => {
// check if the element exists in the accumulator
const ndx = acc.findIndex(e => (Array.isArray(e) ? e.includes(curr) : e === curr));
if (ndx === -1) {
// if it doesn't exist, push it
acc.push(curr);
} else {
// if it exists, check if it's an array
if (Array.isArray(acc[ndx])) acc[ndx].push(curr); // if it is, push the current element
else acc[ndx] = [acc[ndx], curr]; // convert the current element in accumulator to an array with the previous and the new elements
}
return acc;
}, []);
console.log(result);
In order to do that, you need to perform a few operations:
Count occurrence of each number.
Sort non-repeating numbers.
Map it to have either number (non-repeating), or array of repeated numbers.
const array = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20];
Step 1
Let’s count items. For that, I would create an object that will keep a track of each.
const counted = array.reduce(
(acc, current) => Object.assign(acc, { [current]: acc[current] ? acc[current] += 1 : 1 }),
{}
);
Using reduce we create an object, which will look like that:
{
'1': 5,
'2': 3,
'4': 1,
'5': 1,
// etc.
}
Step 2
Let's sort elements:
const sorted = Object.keys(counted)
.map(Number) // <-- we have to remember that object keys are strings
.sort((a, b) => a - b);
Step 3
Now that we have prepared object with counts and have them sorted, let’s put it together. If number in counted is 1 then we’ll insert value directly, otherwise we’ll transform it into array of values.
const final = sorted.map(
(number) => counted[number] == 1
? number
: Array.from({ length: counted[number] }, () => number)
);
You could take an object and use the implicit sorting.
var array = [1, 2, 4, 591, 392, 391, 2, 5, 10, 2, 1, 1, 1, 20, 20],
result = Object.values(array.reduce((r, v) => {
if (v in r) r[v] = [].concat(r[v], v);
else r[v] = v;
return r;
}, {}));
console.log(result);

Check if a JavaScript 2D array is in ascending order?

Suppose I have an array as such
var arr = [ [1,2,3],[4,5,1]]
How do I check if the columns are in ascending order?
For first column 1<4 returns true but the last column returns false as 3 is not less than 1.
The result should return an array of the columns that returns false
I would do it by first transposing the matrix (simply because its easier to deal with in js) and then map over each column (converting them from a set of numbers into a bool using Array#every).
const arr = [
[1,2,3],
[4,5,1]
];
const transpose = (arr) => Array(arr[0].length)
.fill(0)
.map((_, colIndex) => {
return Array(arr.length)
.fill(0)
.map((_, rowIndex) => {
return arr[rowIndex][colIndex];
});
});
const arr2 = transpose(arr);
const arr3 = arr2.map(col => {
let previousValue = -Infinity;
return col.every(v => {
const tmp = previousValue < v;
previousValue = v;
return tmp;
});
});
console.log(arr);
console.log(arr2);
console.log(arr3);
One possible solution is to use Array.map() over the first inner array, and then check if you found some element on a particular column that is not in order using Array.some():
var arr = [
[1, 2, 3],
[4, 5, 1],
[6, 7, 2]
];
let res = arr[0].map((n, cIdx) =>
{
return !arr.some((inner, rIdx) => rIdx > 0 && inner[cIdx] < arr[rIdx - 1][cIdx]);
});
console.log(res);
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}
To find the "columns" with the two values in descending order, you can use parallel arrays. Here is a succinct version of the code for that. (The second snippet is more verbose and provides further explanation.)
var arr = [ [1,2,3], [4,5,1] ], result = [];
arr[0].forEach((n, i) => { // n is the number, i is the index
if(arr[0][i] > arr[1][i]){ result.push(i); } // if descending, remember this index
});
console.log(result); // Logs `[2]` (an array with the index of the third column)
If you want to change which "column" the values are in so that they will be in ascending order, you could do it like this. When you find a "misplaced" value, store it in a temporary variable to make room. Then you can assign the two values to their "correct" locations.
const bigArr = [ [1,2,3], [4,5,1] ],
a1 = bigArr[0],
a2 = bigArr[1],
resultArr = [],
len = Math.min(a1.length, a2.length); // Uses min in case they have different lengths
for(let i = 0; i < len; i++){
if(a1[i] > a2[i]){ // Checks each position, and exchanges values if necessary
exchangeValuesInParallelArrays(a1, a2, i);
resultArr.push(i); // Adds this index to the results array
}
}
console.log(bigArr); // Logs `[ [1,2,1], [4,5,3] ]`
console.log(resultArr) // Logs `[2]`
function exchangeValuesInParallelArrays(arr1, arr2, index){
let tempStorage = arr1[index];
arr1[index] = arr2[index];
arr2[index] = tempStorage;
}

Is there any method to find out which position my new number went to?

I have my array=[5,4,3,1] below, I want to .push(2), then .sort() my array and find out the new number's location in the array that I just pushed. I know the answer the new number's location is in array[1].
var array = [5,4,3,1];
array.push(2); //My new number
var sortedArray = arr.sort();
// sortedArray [1,2,3,4,5]
// The new number's position went to array[1]
Is there any method to find out which position my new number went to?
You could sort an array with indices and take the store index for serarching the inde fo the sorted array.
var array = [5, 4, 3, 1],
index = array.push(2) - 1,
indices = array
.map((_, i) => i)
.sort((a, b) => array[a] - array[b]);
console.log(index); // old index
console.log(indices.indexOf(index)); // new index
console.log(indices);
You can use findIndex.
const array = [5, 4, 3, 1];
const n = 32;
array.push(n);
const sortedArray = array.sort();
const index = sortedArray.findIndex(el => el === n);
console.log(sortedArray, index)
Note that you sort could be improved if you're using numbers and you want them to be in ascending order after the sort:
const sortedArray = array.sort((a, b) => b < a);
You can use reduce function.
This approach returns an array of indexes of number 2 if this is repeated.
In this case, will return an array with only one index.
Look at this code snippet
var array = [5,4,3,1];
array.push(2);
var indexes = array.sort().reduce((a, n, i) => {
if (n === 2) {
a.push(i);
}
return a;
}, []);
console.log(JSON.stringify(indexes));
See, returns an array with only one index.
Resource
Array.prototype.reduce()

Categories

Resources