Javascript group array by intersection - javascript

Given an array of paired integers, HOW can I group by intersections. Does anyone have a simple function that could convert my input, into the desired output?
Input
var in = ["0:3", "1:3", "4:5", "5:6", "6:8"]
Desired output
[
[0, 1, 3],
[4, 5, 6, 8]
]
UPDATE:
#apsiller asked my question in the comments more clearly then I originally posted:
"Considering each number as a node in a graph, and each pairing x:y as an edge between nodes x and y, find the sets of numbers that can be traveled to using the edges defined. That is, in graph theory terms, find the distinct connected components within such a graph.
For instance, there is no way to travel from 4 to 0 so they are in different groups, but there is a way to travel from 1 to 0 (by way of 3) so they are in the same group."
To reiterate the desired output is a grouping of transversable nodes, based on a potentially random input set.

Thanks everyone. Given everyones input I was able to find a similar question on here that led me my answer. Finding All Connected Components of an Undirected Graph
The first step was to change my input to groups of pairs.
var input = [
[0, 3],
[1, 3],
[4, 5],
[5, 6],
[6, 8]
]
The next step was to use whats called Breadth-first search
function breadthFirstSearch(node, nodes, visited) {
var queue = [];
var group = [];
var pair = null;
queue.push(node);
while (queue.length > 0) {
node = queue.shift();
if (!visited[node]) {
visited[node] = true;
group.push(node);
for (var i = 0, len = nodes.length; i < len; i++) {
pair = nodes[i];
if (pair[0] === node && !visited[pair[1]]) {
queue.push(pair[1]);
} else if (pair[1] === node && !visited[pair[0]]) {
queue.push(pair[0]);
}
}
}
}
return group;
};
function groupReachableVertices(input) {
var groups = [];
var visited = {};
for (var i = 0, len = input.length; i < len; i += 1) {
var current_pair = input[i];
var u = current_pair[0];
var v = current_pair[1];
var src = null;
if (!visited[u]) {
src = u;
} else if (!visited[v]) {
src = v;
}
if (src) {
groups.push(breadthFirstSearch(src, input, visited));
}
}
return groups;
};
Putting it all together...
var output = groupReachableVertices(input);
[
[0, 1, 3],
[4, 5, 6, 8]
]

You could do something like this.
function group(data) {
var r = [[]],c = 0,a = [0]
var d = data.map(e => e.split(':').sort((a, b) => a - b)).sort((a, b) => a[0] - b[0])
d.forEach(function(e, i) {
if (e[0] > a[a.length - 1]) {
r.push(e)
a.push(e[1])
c++
} else {
r[c] = r[c].concat(e)
a[a.length - 1] = e[1]
}
})
return r.map(e => [...new Set(e)].sort((a, b) => a - b))
}
var test1 = ["0:3", "1:3", "4:5", "5:6", "6:8"]
var test2 = ["0:3", "1:3", "4:5", "9:11", "10:12", '3:6', "7:8"]
var test3 = ["20:15", "4:0", "1:3", "5:1", "9:11", "10:12", '3:6', "8:7"]
console.log(JSON.stringify(group(test1)))
console.log(JSON.stringify(group(test2)))
console.log(JSON.stringify(group(test3)))

You could use a hash table and collect all nodes in it. It works for any values.
var data = ["0:3", "1:3", "4:5", "a:8", "5:a"],
result = data
.map(function (a) { return a.split(':'); })
.reduce(function (hash) {
return function (r, a) {
if (hash[a[0]] && hash[a[1]]) {
hash[a[0]].push.apply(hash[a[0]], r.splice(r.indexOf(hash[a[1]]), 1)[0]);
hash[a[1]] = hash[a[0]];
return r;
}
if (hash[a[0]]) {
hash[a[1]] = hash[a[0]];
hash[a[1]].push(a[1]);
return r;
}
if (hash[a[1]]) {
hash[a[0]] = hash[a[1]];
hash[a[0]].push(a[0]);
return r;
}
hash[a[0]] = a.slice();
hash[a[1]] = hash[a[0]];
return r.concat([hash[a[0]]]);
};
}(Object.create(null)), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

I guess, by using Object.values() and Set object you can simply do as follows in ES6.
function getConnectedVertices(a){
return [...new Set(Object.values(a.reduce((h,v) => (h[v[0]] ? h[v[1]] ? (h[v[0]] = h[v[0]].concat(h[v[1]]),
h[v[1]] = h[v[0]])
: (h[v[0]].push(v[1]),
h[v[1]] = h[v[0]])
: h[v[1]] ? (h[v[1]].push(v[0]),
h[v[0]] = h[v[1]])
: h[v[0]] = h[v[1]] = v,
h),{})))];
}
var input = ["0:3", "1:3", "4:5", "5:6", "6:8"].map(s => s.split(":")),
result = getConnectedVertices(input);
console.log(result);

Related

Get every nth element from array and map/push to new arrays [duplicate]

I've got an array of arrays, something like:
[
[1,2,3],
[1,2,3],
[1,2,3],
]
I would like to transpose it to get the following array:
[
[1,1,1],
[2,2,2],
[3,3,3],
]
It's not difficult to programmatically do so using loops:
function transposeArray(array, arrayLength){
var newArray = [];
for(var i = 0; i < array.length; i++){
newArray.push([]);
};
for(var i = 0; i < array.length; i++){
for(var j = 0; j < arrayLength; j++){
newArray[j].push(array[i][j]);
};
};
return newArray;
}
This, however, seems bulky, and I feel like there should be an easier way to do it. Is there?
output = array[0].map((_, colIndex) => array.map(row => row[colIndex]));
map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results. callback is invoked only for indexes of the array which have assigned values; it is not invoked for indexes which have been deleted or which have never been assigned values.
callback is invoked with three arguments: the value of the element, the index of the element, and the Array object being traversed. [source]
Many good answers here! I consolidated them into one answer and updated some of the code for a more modern syntax:
One-liners inspired by Fawad Ghafoor and Óscar Gómez Alcañiz
function transpose(matrix) {
return matrix[0].map((col, i) => matrix.map(row => row[i]));
}
function transpose(matrix) {
return matrix[0].map((col, c) => matrix.map((row, r) => matrix[r][c]));
}
Functional approach style with reduce by Andrew Tatomyr
function transpose(matrix) {
return matrix.reduce((prev, next) => next.map((item, i) =>
(prev[i] || []).concat(next[i])
), []);
}
Lodash/Underscore by marcel
function tranpose(matrix) {
return _.zip(...matrix);
}
// Without spread operator.
function transpose(matrix) {
return _.zip.apply(_, [[1,2,3], [1,2,3], [1,2,3]])
}
Even simpler Lodash/Underscore solution by Vigrant
_.unzip(matrix);
Vanilla approach
function transpose(matrix) {
const rows = matrix.length, cols = matrix[0].length;
const grid = [];
for (let j = 0; j < cols; j++) {
grid[j] = Array(rows);
}
for (let i = 0; i < rows; i++) {
for (let j = 0; j < cols; j++) {
grid[j][i] = matrix[i][j];
}
}
return grid;
}
Vanilla in-place ES6 approach inspired by Emanuel Saringan
function transpose(matrix) {
for (var i = 0; i < matrix.length; i++) {
for (var j = 0; j < i; j++) {
const temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
}
// Using destructing
function transpose(matrix) {
for (var i = 0; i < matrix.length; i++) {
for (var j = 0; j < i; j++) {
[matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]];
}
}
}
here is my implementation in modern browser (without dependency):
transpose = m => m[0].map((x,i) => m.map(x => x[i]))
You could use underscore.js
_.zip.apply(_, [[1,2,3], [1,2,3], [1,2,3]])
shortest way with lodash/underscore and es6:
_.zip(...matrix)
where matrix could be:
const matrix = [[1,2,3], [1,2,3], [1,2,3]];
Neat and pure:
[[0, 1], [2, 3], [4, 5]].reduce((prev, next) => next.map((item, i) =>
(prev[i] || []).concat(next[i])
), []); // [[0, 2, 4], [1, 3, 5]]
Previous solutions may lead to failure in case an empty array is provided.
Here it is as a function:
function transpose(array) {
return array.reduce((prev, next) => next.map((item, i) =>
(prev[i] || []).concat(next[i])
), []);
}
console.log(transpose([[0, 1], [2, 3], [4, 5]]));
Update.
It can be written even better with spread operator:
const transpose = matrix => matrix.reduce(
($, row) => row.map((_, i) => [...($[i] || []), row[i]]),
[]
)
You can do it in in-place by doing only one pass:
function transpose(arr,arrLen) {
for (var i = 0; i < arrLen; i++) {
for (var j = 0; j <i; j++) {
//swap element[i,j] and element[j,i]
var temp = arr[i][j];
arr[i][j] = arr[j][i];
arr[j][i] = temp;
}
}
}
Just another variation using Array.map. Using indexes allows to transpose matrices where M != N:
// Get just the first row to iterate columns first
var t = matrix[0].map(function (col, c) {
// For each column, iterate all rows
return matrix.map(function (row, r) {
return matrix[r][c];
});
});
All there is to transposing is mapping the elements column-first, and then by row.
Another approach by iterating the array from outside to inside and reduce the matrix by mapping inner values.
const
transpose = array => array.reduce((r, a) => a.map((v, i) => [...(r[i] || []), v]), []),
matrix = [[1, 2, 3], [1, 2, 3], [1, 2, 3]];
console.log(transpose(matrix));
If you have an option of using Ramda JS and ES6 syntax, then here's another way to do it:
const transpose = a => R.map(c => R.map(r => r[c], a), R.keys(a[0]));
console.log(transpose([
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
])); // => [[1,5,9],[2,6,10],[3,7,11],[4,8,12]]
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.22.1/ramda.min.js"></script>
If using RamdaJS is an option, this can be achieved in one line: R.transpose(myArray)
Spread syntax should not be used as an alternative to push, it should only be used when you don't want to mutate the existing array.
Algorithm:
For every column, just check if for that column there's a row in the resultant matrix, if there's already a row then simply push the element, else create a new row array and then push.
So, unlike many other solutions above, this solution doesn't create new arrays again and again, instead pushes onto the same array.
Also, take some time to appreciate the use of the Nullish Coalescing Operator.
const
transpose = arr => arr.reduce((m, r) => (r.forEach((v, i) => (m[i] ??= [], m[i].push(v))), m), []),
matrix = [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
console.log(transpose(matrix))
You can achieve this without loops by using the following.
Array
Array.prototype.map
Array.prototype.reduce
Array.prototype.join
String.prototype.split
It looks very elegant and it does not require any dependencies such as jQuery of Underscore.js.
function transpose(matrix) {
return zeroFill(getMatrixWidth(matrix)).map(function(r, i) {
return zeroFill(matrix.length).map(function(c, j) {
return matrix[j][i];
});
});
}
function getMatrixWidth(matrix) {
return matrix.reduce(function (result, row) {
return Math.max(result, row.length);
}, 0);
}
function zeroFill(n) {
return new Array(n+1).join('0').split('').map(Number);
}
Minified
function transpose(m){return zeroFill(m.reduce(function(m,r){return Math.max(m,r.length)},0)).map(function(r,i){return zeroFill(m.length).map(function(c,j){return m[j][i]})})}function zeroFill(n){return new Array(n+1).join("0").split("").map(Number)}
Here is a demo I threw together. Notice the lack of loops :-)
// Create a 5 row, by 9 column matrix.
var m = CoordinateMatrix(5, 9);
// Make the matrix an irregular shape.
m[2] = m[2].slice(0, 5);
m[4].pop();
// Transpose and print the matrix.
println(formatMatrix(transpose(m)));
function Matrix(rows, cols, defaultVal) {
return AbstractMatrix(rows, cols, function(r, i) {
return arrayFill(cols, defaultVal);
});
}
function ZeroMatrix(rows, cols) {
return AbstractMatrix(rows, cols, function(r, i) {
return zeroFill(cols);
});
}
function CoordinateMatrix(rows, cols) {
return AbstractMatrix(rows, cols, function(r, i) {
return zeroFill(cols).map(function(c, j) {
return [i, j];
});
});
}
function AbstractMatrix(rows, cols, rowFn) {
return zeroFill(rows).map(function(r, i) {
return rowFn(r, i);
});
}
/** Matrix functions. */
function formatMatrix(matrix) {
return matrix.reduce(function (result, row) {
return result + row.join('\t') + '\n';
}, '');
}
function copy(matrix) {
return zeroFill(matrix.length).map(function(r, i) {
return zeroFill(getMatrixWidth(matrix)).map(function(c, j) {
return matrix[i][j];
});
});
}
function transpose(matrix) {
return zeroFill(getMatrixWidth(matrix)).map(function(r, i) {
return zeroFill(matrix.length).map(function(c, j) {
return matrix[j][i];
});
});
}
function getMatrixWidth(matrix) {
return matrix.reduce(function (result, row) {
return Math.max(result, row.length);
}, 0);
}
/** Array fill functions. */
function zeroFill(n) {
return new Array(n+1).join('0').split('').map(Number);
}
function arrayFill(n, defaultValue) {
return zeroFill(n).map(function(value) {
return defaultValue || value;
});
}
/** Print functions. */
function print(str) {
str = Array.isArray(str) ? str.join(' ') : str;
return document.getElementById('out').innerHTML += str || '';
}
function println(str) {
print.call(null, [].slice.call(arguments, 0).concat(['<br />']));
}
#out {
white-space: pre;
}
<div id="out"></div>
I found the above answers either hard to read or too verbose, so I write one myself. And I think this is most intuitive way to implement transpose in linear algebra, you don't do value exchange, but just insert each element into the right place in the new matrix:
function transpose(matrix) {
const rows = matrix.length
const cols = matrix[0].length
let grid = []
for (let col = 0; col < cols; col++) {
grid[col] = []
}
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
grid[col][row] = matrix[row][col]
}
}
return grid
}
Edit: This answer would not transpose the matrix, but rotate it. I didn't read the question carefully in the first place :D
clockwise and counterclockwise rotation:
function rotateCounterClockwise(a){
var n=a.length;
for (var i=0; i<n/2; i++) {
for (var j=i; j<n-i-1; j++) {
var tmp=a[i][j];
a[i][j]=a[j][n-i-1];
a[j][n-i-1]=a[n-i-1][n-j-1];
a[n-i-1][n-j-1]=a[n-j-1][i];
a[n-j-1][i]=tmp;
}
}
return a;
}
function rotateClockwise(a) {
var n=a.length;
for (var i=0; i<n/2; i++) {
for (var j=i; j<n-i-1; j++) {
var tmp=a[i][j];
a[i][j]=a[n-j-1][i];
a[n-j-1][i]=a[n-i-1][n-j-1];
a[n-i-1][n-j-1]=a[j][n-i-1];
a[j][n-i-1]=tmp;
}
}
return a;
}
const transpose = array => array[0].map((r, i) => array.map(c => c[i]));
console.log(transpose([[2, 3, 4], [5, 6, 7]]));
ES6 1liners as :
let invert = a => a[0].map((col, c) => a.map((row, r) => a[r][c]))
so same as Óscar's, but as would you rather rotate it clockwise :
let rotate = a => a[0].map((col, c) => a.map((row, r) => a[r][c]).reverse())
let a = [
[1,1,1]
, ["_","_","1"]
]
let b = rotate(a);
let c = rotate(b);
let d = rotate(c);
console.log(`a ${a.join("\na ")}`);
console.log(`b ${b.join("\nb ")}`);
console.log(`c ${c.join("\nc ")}`);
console.log(`d ${d.join("\nd ")}`);
Yields
a 1,1,1
a _,_,1
b _,1
b _,1
b 1,1
c 1,_,_
c 1,1,1
d 1,1
d 1,_
d 1,_
I think this is slightly more readable. It uses Array.from and logic is identical to using nested loops:
var arr = [
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4]
];
/*
* arr[0].length = 4 = number of result rows
* arr.length = 3 = number of result cols
*/
var result = Array.from({ length: arr[0].length }, function(x, row) {
return Array.from({ length: arr.length }, function(x, col) {
return arr[col][row];
});
});
console.log(result);
If you are dealing with arrays of unequal length you need to replace arr[0].length with something else:
var arr = [
[1, 2],
[1, 2, 3],
[1, 2, 3, 4]
];
/*
* arr[0].length = 4 = number of result rows
* arr.length = 3 = number of result cols
*/
var result = Array.from({ length: arr.reduce(function(max, item) { return item.length > max ? item.length : max; }, 0) }, function(x, row) {
return Array.from({ length: arr.length }, function(x, col) {
return arr[col][row];
});
});
console.log(result);
I didn't find an answer that satisfied me, so I wrote one myself, I think it is easy to understand and implement and suitable for all situations.
transposeArray: function (mat) {
let newMat = [];
for (let j = 0; j < mat[0].length; j++) { // j are columns
let temp = [];
for (let i = 0; i < mat.length; i++) { // i are rows
temp.push(mat[i][j]); // so temp will be the j(th) column in mat
}
newMat.push(temp); // then just push every column in newMat
}
return newMat;
}
Since nobody so far mentioned a functional recursive approach here is my take. An adaptation of Haskell's Data.List.transpose.
var transpose = as => as.length ? as[0].length ? [as.reduce((rs, a) => a.length ? (rs.push(a[0]), rs) :
rs, []
), ...transpose(as.map(a => a.slice(1)))] :
transpose(as.slice(1)) :
[],
mtx = [
[1],
[1, 2],
[1, 2, 3]
];
console.log(transpose(mtx))
.as-console-wrapper {
max-height: 100% !important
}
Adding TS version here.
const transpose = <T>(m: Array<Array<T>>): Array<Array<T>> => m[0].map((_, i) => m.map(x => x[i]));
One-liner that does not change given array.
a[0].map((col, i) => a.map(([...row]) => row[i]))
This one, is not only a super efficient one, but a pretty short solution.
Algorithm Time Complexity: O(n log n)
const matrix = [
[1,1,1,1],
[2,2,2,2],
[3,3,3,3],
[4,4,4,4]
];
matrix.every((r, i, a) => (
r.every((_, j) => (
j = a.length-j-1,
[ r[j], a[j][i] ] = [ a[j][i], r[j] ],
i < j-1
)),
i < length-2
));
console.log(matrix);
/*
Prints:
[
[1,2,3,4],
[1,2,3,4],
[1,2,3,4],
[1,2,3,4]
]
*/
The example above will do only 6 iterations.
For bigger matrix, say 100x100 it will do 4,900 iterations, this is 51% faster than any other solution provided here.
The principle is simple, you on only iterate through the upper diagonal half of the matrix, because the diagonal line never changes and the bottom diagonal half being is switched together with the upper one, so there is no reason to iterate through it as well. This way, you save a lot of running time, especially in a large matrix.
function invertArray(array,arrayWidth,arrayHeight) {
var newArray = [];
for (x=0;x<arrayWidth;x++) {
newArray[x] = [];
for (y=0;y<arrayHeight;y++) {
newArray[x][y] = array[y][x];
}
}
return newArray;
}
reverseValues(values) {
let maxLength = values.reduce((acc, val) => Math.max(val.length, acc), 0);
return [...Array(maxLength)].map((val, index) => values.map((v) => v[index]));
}

Inconsistency, when returning index of duplicate values

I'm trying to create an algorithm to find duplicate values in a list and return their respective indexes, but the script only returns the correct value, when I have 2 equal elements:
array = [1,2,0,5,0]
result -> (2) [2,4]
Like the example below:
array = [0,0,2,7,0];
result -> (6) [0, 1, 0, 1, 0, 4]
The expected result would be [0,1,4]
Current code:
const numbers = [1,2,0,5,0];
const checkATie = avgList => {
let averages, tie, n_loop, currentAverage;
averages = [... avgList];
tie = [];
n_loop = 0;
for(let n = 0; n <= averages.length; n++) {
currentAverage = parseInt(averages.shift());
n_loop++
for(let avg of averages) {
if(avg === currentAverage) {
tie.push(numbers.indexOf(avg),numbers.indexOf(avg,n_loop))
};
};
};
return tie;
}
console.log(checkATie(numbers));
if possible I would like to know some way to make this code more concise and simple
Use a Set
return [...new Set(tie)]
const numbers1 = [1,2,0,5,0];
const numbers2 = [0,0,2,7,0];
const checkATie = avgList => {
let averages, tie, n_loop, currentAverage;
averages = [... avgList];
tie = [];
n_loop = 0;
for(let n = 0; n <= averages.length; n++) {
currentAverage = parseInt(averages.shift());
n_loop++
for(let avg of averages) {
if(avg === currentAverage) {
tie.push(avgList.indexOf(avg),avgList.indexOf(avg,n_loop))
};
};
};
return [...new Set(tie)]
}
console.log(checkATie(numbers1));
console.log(checkATie(numbers2));
I hope this help you.you can use foreach function to check each item of array
var array = [0,0,2,7,0];
var result = [] ;
array.forEach((item , index)=>{
if(array.findIndex((el , i )=> item === el && index !== i ) > -1 ){
result.push(index)
}
})
console.log(result);
//duplicate entries as an object
checkDuplicateEntries = (array) => {
const duplicates = {};
for (let i = 0; i < array.length; i++) {
if (duplicates.hasOwnProperty(array[i])) {
duplicates[array[i]].push(i);
} else if (array.lastIndexOf(array[i]) !== i) {
duplicates[array[i]] = [i];
}
}
console.log(duplicates);
}
checkDuplicateEntries([1,2,0,5,0]);
// hope this will help
Create a lookup object with value and their indexes and then filter all the values which occurred more than once and then merge all indexes and generate a new array.
const array = [1, 2, 0, 5, 0, 1, 0, 2],
result = Object.values(array.reduce((r, v, i) => {
r[v] = r[v] || [];
r[v].push(i);
return r;
}, {}))
.filter((indexes) => indexes.length > 1)
.flatMap(x => x);
console.log(result);

Sort a 2D Array/Grid/Table of items in Javascript

I have an inventory of items in my game and the player will need to have the ability to auto-sort the items based on several criteria being name,quantity and type.
// create the Inventory grid
var InventoryWidth = 2;
var InventoryHeight = 4;
var Inventory = new Array(InventoryWidth);
for (var i = 0; i < InventoryWidth; i++) {
Inventory[i] = new Array(InventoryHeight);
}
// set the Items & default quantities
Inventory[0][0] = "Potion";
Inventory[1][0] = 2;
Inventory[0][1] = "Elixir";
Inventory[1][1] = 9;
Inventory[0][2] = "Antidote";
Inventory[1][2] = 5;
Inventory[0][3] = "Ether";
Inventory[1][3] = 1;
// function for sorting items
function Sort2D(array2D, byColumn, ascending) {
// sort, seems I am using the wrong sorting function or my approach is wrong here:
// not sure how to do ASC/DESC as well
array2D.sort(function(a, b)
{
if(a[0] === b[0])
{
var x = a[byColumn].toLowerCase(), y = b[byColumn].toLowerCase();
return x < y ? -1 : x > y ? 1 : 0;
}
return a[0] - b[0];
});
}
// sort all rows by first column: "name", setting to 1 should compare and sort the quantities instead
Sort2D( Inventory, 0, true);
// print grid contents
var output = "";
for(var i = 0; i < InventoryHeight; i++) {
if (i == 0) {
output += " | name | own |";
}
for(var j = 0; j < InventoryWidth; j++) {
if (j == 0) {
output += "\n"+i+"|";
}
output+=Inventory[j][i];
if (j >= Inventory[0].length-1) {
output += "|\n";
} else {
output += ", ";
}
}
}
console.log(output);
However I can't seem to figure out how to sort the grid like a table of items.
I'd need it to sort the row order by a chosen column and the ability to have it in ASC/DESC order. How would I go about this?
To sort an array alphabetically, you just need to use the localeCompare method. Numbers have their own version and that can be confusing, so we coerce the variable before comparing it.
function sortAlphabetically(a, b) {
return String(a).localeCompare(b);
}
["cat", "apple", "dog", "beef"].sort(sortAlphabetically);
// -> ["apple", "beef", "cat", "dog"]
I think the main problem you're having is actually with the way you've created your array. At the moment, your array looks like this:
var inventory = [
["Potion", "Elixir", "Antidote", "Ether"],
[2, 9, 5, 1]
];
That means there's no association between "Potion" and 2, other than the array indicies. I think you'll have much better luck if you adjust the array to look like this.
var inventory = [
["Potion", 2],
["Elixir", 9],
["Antidote", 5],
["Ether", 1]
];
Sorting that is much easier. As a bonus, running the .concat() method will clone the array before trying to sort it so the original data isn't modified and having the function return the data in ascending order by default is more conventional.
function sort2D(array, byColumn, isDescending) {
var sorted = array.concat().sort(function (a, b) {
return typeof a[byColumn] === "string"
? sortAlphabetically(a[byColumn], b[byColumn])
: a[byColumn] - b[byColumn];
});
return isDescending
? sorted.reverse()
: sorted;
}
sort2D(inventory, 0);
// -> [ ["Antidote", 5], ["Elixir", 9], ["Ether", 1], ["Potion", 2] ]
sort2D(inventory, 0, true);
// -> [ ["Potion", 2], ["Ether", 1], ["Elixir", 9], ["Antidote", 5] ]
sort2D(inventory, 1);
// -> [ ["Ether", 1], ["Potion", 2], ["Antidote", 5], ["Elixir", 9] ]
I hope that helps.
Update:
Logging out your information becomes easier as well:
var output = inventory
.map(function (inv) {
return "| " + inv.join(" | ") + " |";
})
.join("\n");
console.log("| name | own |\n" + output);
Update 2:
Here's how to sort the old data.
function sort2D(array, byColumn, isDescending) {
// Step 1: sort the part of the array you're trying to sort.
var preSort = array[byColumn].concat().sort(function (a, b) {
return typeof a === "string"
? sortAlphabetically(a, b)
: a - b;
});
if (isDescending) {
preSort = preSort.reverse();
}
// Step 2: create a new, sorted array with your sorted column.
var sorted = [];
sorted[byColumn] = preSort;
// Step 3: create a map to show how the array way sorted.
var sortMap = {};
preSort.forEach(function (item, i) {
sortMap[array[byColumn].indexOf(item)] = i;
});
// Step 4: manually sort the other items of the array.
array.forEach(function (info, i) {
var copy = [];
if (i !== byColumn) {
info.forEach(function (item, j) {
copy[sortMap[j]] = item;
});
sorted[i] = copy;
}
});
// Step 5: return the newly sorted array.
return sorted;
}

use lodash to remove elements from nested array form start and from end

I have one nested array for example
var arr = [
[0,1,2,3,4],
[0,1,2,3],
[0,1,2,3,4],
[0,1]
];
How to remove N items from end or from beginning using lodash?
For example if I remove 6 elements from beginning, I want result to be:
var arr = [
[1,2,3],
[0,1,2,3,4],
[0,1]
];
and if I remove 1 from end, I need result to be:
var arr = [
[0,1,2,3,4],
[0,1,2,3],
[0,1,2,3,4],
[0]
];
I hope i was clear. Lodash is not necessary.
This is my code:
function removeFromTop(group, count) {
for (var i = 0; i < group.length; i++) {
for (var x = 0; x < group[i].chatItems.length; x++) {
if(count) {
group[i].chatItems.splice(x, 1);
if(!group[i].chatItems.length) {
group.splice(i, 1);
};
count--;
} else {
break;
}
};
};
return group;
}
function removeFromBottom(group, count) {
for (var i = group.length - 1; i >= 0; i--) {
for (var x = group[i].chatItems.length - 1; x >= 0; x--) {
if(count) {
group[i].chatItems.splice(x, 1);
if(!group[i].chatItems.length) {
group.splice(i, 1);
};
count--;
} else {
break;
}
};
};
return group;
}
You could shift the inner array for each item count from the beginning and pop the values from the end. For the first you could use Array#reduce and for the other Array#reduceRight
function removeFromStart(array, n) {
var copy = JSON.parse(JSON.stringify(array));
return copy.reduce(function (r, a) {
while (n && a.length) {
a.shift();
n--;
}
a.length && r.push(a);
return r;
}, []);
}
function removeFromEnd(array, n) {
var copy = JSON.parse(JSON.stringify(array));
return copy.reduceRight(function (r, a) {
while (n && a.length) {
a.pop();
n--;
}
a.length && r.push(a);
return r;
}, []).reverse();
}
var array = [[0, 1, 2, 3, 4], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1]];
console.log(JSON.stringify(removeFromStart(array, 6)));
console.log(JSON.stringify(removeFromEnd(array, 6)));
.as-console-wrapper { max-height: 100% !important; top: 0; }
using Lodash function .drop you can drop very first element(s) of an array or else can specify n element(s) default value is 1. same way .dropRight for the end element(s).
var arr = [
[0,1,2,3,4],
[0,1,2,3],
[0,1,2,3,4],
[0,1]
];
// remove 1 element front of 2D Array
var resultFront= arr.map(function(value,index) { return _.drop(value); });
console.log(resultFront);
// remove 1 element from End of 2D Array
var resultEnd= arr.map(function(value,index) { return _.dropRight(value); });
console.log(resultEnd);
// remove 1 element front and end of 2D Array
var resultFrontEnd = arr.map(function(value,index) { return _.dropRight(_.drop(value)); });
console.log(resultFrontEnd);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
var arr = [
[0,1,2,3,4],
[0,1,2,3],
[0,1,2,3,4],
[0,1]
];
console.log(arr);
console.log('------------------------');
// remove 1 element front of 2D Array
var resultFront= arr.map(function(value,index) { return _.drop(value); });
console.log('Remove 1 element from front') ;
console.log(resultFront);
// remove 1 element from End of 2D Array
var resultEnd= arr.map(function(value,index) { return _.dropRight(value); });
console.log('Remove 1 element from end') ;
console.log(resultEnd);
// remove 1 element front and end of 2D Array
var resultFrontEnd = arr.map(function(value,index) { return _.dropRight(_.drop(value)); });
console.log('Remove 1 element from front & End') ;
console.log(resultFrontEnd);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
You can simply do as follows;
var arr = [[0,1,2,3,4],[0,1,2,3],[0,1,2,3,4],[0,1]],
r = [],
n = 6,
res = arr.reduce((r,sa) => r.n > sa.length ? (r.n -= sa.length, r)
: (r.push(sa.slice(r.n)), r.n = 0, r), (r.n = n, r));
console.log(res);
I use a state variable r.n within the initial array in the reduce operation. You may or may not chose to delete it afterwards.

Read an array with an array in Javascript?

If a=[[1,[],"f",3],[3,[4,"x"]]] and b=[1,1].
I want to read a by b like a[1][1] to get [4,"x"]. Note that b is an array which should only consist of integers.
You could also do eval('a['+b.join('],[')+']') but requires the actual variable name as string and it's ugly.
Here are some of my functions:
Array.prototype.readByArray = function(a) {
var c = this;
for (var i = 0; i < a.length; i++) {
c = c[a[i]];
}
return c;
};
Array.prototype.emptyByArray = function(a) {
var c = this.readByArray(a);
c.splice(0, c.length);
};
Array.prototype.concateByArray = function(a, e) {
var c = this.readByArray(a);
for (var i = 0; i < e.length; i++) {
c.push(e[i]);
}
};
Array.prototype.setByArray = function(a, e) {
this.emptyByArray(a);
this.readByArray(a).push(e);
};
This could be useful for reading a nested array in an imperative way in this example:
Array.prototype.readByArray=function(a){var c=this;for(var i=0;i<a.length;i++){c=c[a[i]];}return c;};
var a = [1,2,3,[1,2,3,[{x: 3},"test"],4],"foo","bar"]; //Your array
var b = [0]; //Reading stack
var s = '[\n'; //Output
while(b[0]<a.length){
if(Array.isArray(a.readByArray(b))){
s+=' '.repeat(b.length)+'[\n';
b.push(-1);
}else{
s+=' '.repeat(b.length)+JSON.stringify(a.readByArray(b))+'\n';
}
b[b.length-1]++;
while(b[b.length-1]>=a.readByArray(b.slice(0,-1)).length){
b.pop();
b[b.length-1]++;
s+=' '.repeat(b.length)+']\n';
}
}
console.log(s);
Is there any better way to do this? Are there native functions for this?
You could use Array#reduce for it.
You start with the whole array and return for every element of b a part of the array until all indices are used.
var a = [[1, [], "f", 3], [3, [4, "x"]]],
b = [1, 1],
result = b.reduce(function (v, i) {
return v[i];
}, a);
console.log(result);
ES6
var a = [[1, [], "f", 3], [3, [4, "x"]]],
b = [1, 1],
result = b.reduce((v, i) => v[i], a);
console.log(result);
result[0] = 42;
console.log(a);
result.splice(0, result.length, 'test');
console.log(a);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I had written a reusable generic code exactly for this purpose to get the nested object properties dynamically. Actually i was targeting objects but since in JS an array is a perfect object it also applies for arrays too. So lets see how it works in this particular case;
Object.prototype.getNestedValue = function(...a) {
return a.length > 1 ? (this[a[0]] !== void 0 && this[a[0]].getNestedValue(...a.slice(1))) : this[a[0]];
};
var a = [[1,[],"f",3],[3,[4,"x"]]],
b = [1,1],
c = a.getNestedValue(...b);
console.log(c)

Categories