Sorting columns in a multidimensional Array - javascript

I want to sort a multidimensional array using the first row,
I want that the second and the third row get the same sort order of the first row
Here is an example of array :
var arr =
[
[1,3,5,6,0,2],
[1,2,7,4,0,6],
[1,2,3,4,5,6]
]
the result that I want =
[
[0,1,2,3,5,6],
[0,1,6,2,7,4],
[5,1,6,2,3,4]
]
I tried the following function but it doesnt work as I want
arr = arr.sort(function(a,b) {
return a[0] > b[0];
});
What's wrong with my function ?
Thank you

I think I understand what you're looking for:
a = [
[1,3,5,6,0,2],
[1,2,7,4,0,6],
[1,2,3,4,5,6]
]
transpose = m => m[0].map((_, c) => m.map(r => r[c]));
z = transpose(transpose(a).sort((x, y) => x[0] - y[0]))
z.map(r =>
document.write('<pre>'+JSON.stringify(r) + '</pre>'));
In ES5
transpose = function(m) {
return m[0].map(function (_, c) {
return m.map(function (r) {
return r[c]
})
})
};
z = transpose(transpose(a).sort(function (x, y) { return x[0] - y[0] }));
UPD: the transpose trick is kinda "smart", but hardly efficient, if your matrices are big, here's a faster way:
a = [
[1,3,5,6,0,2],
[1,2,7,4,0,6],
[1,2,3,4,5,6]
]
t = a[0].map((e, i) => [e, i]).sort((x, y) => x[0] - y[0]);
z = a.map(r => t.map(x => r[x[1]]));
z.map(r =>
document.write('<pre>'+JSON.stringify(r) + '</pre>'));
Basically, sort the first row and remember indexes, then for each row, pick values by an index.

A bit more verbose than previous answer in ES5
var arr = [
[1, 3, 5, 6, 0, 2],
[1, 2, 7, 4, 0, 6],
[1, 2, 3, 4, 5, 6]
]
var sortIndices = arr[0].slice().sort(function(a, b) {
return a - b;
}).map(function(el) {
return arr[0].indexOf(el)
});
arr = arr.reduce(function(a, c) {
var sorted = sortIndices.map(function(i) {
return c[i];
});
a.push(sorted);
return a;
}, []);
document.getElementById('pre').innerHTML = JSON.stringify(arr)
<pre id="pre"></pre>

Related

How to compare values of array and if they are equal make them one

Hi I want to compare values in arrays and if they are same I want them to be considered as one.
var arrA = [1,1,1,1,2,2,2,3,3,4,4]
var arrB = [0.1,0.1,0.1,0.1 ,0.2,0.2,0.2 ,0.3,0.3 ,0.4,0.4]
What I want to do is to compare values of arrA if arrA[i] == arrA[i+1] then it should consider it as one value and in arrB the values of arrB[i] adds up with arrB[i+1].
My final array shall look like this:
var arrA = [1,2,3,4]
var arrB = [0.4,0.6,0.6,0.8]
Can anyone help me with the javascript code for this. Thank you!
You could reduce the array and check if the previous value is equal to the actual value.
function sumSame(arrayA, arrayB) {
var i,
l = arrayA.length,
resultA = [],
resultB = [];
for (i = 0; i < l; i++) {
if (arrayA[i] === arrayA[i - 1]) {
resultB[resultB.length - 1] += arrayB[i];
} else {
resultA.push(arrayA[i]);
resultB.push(arrayB[i]);
}
}
return [resultA, resultB];
}
console.log(sumSame(
[1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4],
[0.1, 0.1, 0.1, 0.1, 0.2, 0.2, 0.2, 0.3, 0.3, 0.4, 0.4]
));
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can create a function with reduce method that takes an array and one more parameter that you can set to true or false and based on it you can just filter duplicates or also sum.
const transform = (data, sum = false) => data.reduce((r, e, i) => {
if (e != data[i - 1]) r.push(e)
else {
if (sum) r[r.length - 1] += e
}
return r;
}, []);
console.log(transform([1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4]));
console.log(transform([0.1, 0.1, 0.1, 0.1, 0.2, 0.2, 0.2, 0.3, 0.3, 0.4, 0.4], true));
For arrAYou can use Set like below
const arr = [1,2,3,4,1,2,3,4.....];
const uniqueArr= Array.from(new Set(arr));
if not to use any other objects except array then for the first part of your question
const arrA = [1,1,1,1,2,2,2,3,3,4,4]
const arrAUnique = arrA
.sort((a, b) => a - b)
.filter((v, ind, arr) => v !== arr[ind - 1])
For your second question solution with only array use
var arrB = [0.1,0.1,0.1,0.1 ,0.2,0.2,0.2 ,0.3,0.3 ,0.4,0.4]
var arrBSum = arrB
.sort((a, b) => a - b)
.reduce((newArr, v, ind, arr) => {
if (v !== arr[ind - 1]) {
newArr.push(v)
} else {
newArr[newArr.length - 1] += v
}
return newArr
}, [])
This may be a clean solution
// Find unique
const unique = arrA.reduce((result, curr) => {
if(result.indexOf(curr)===-1) result.push(curr);
return result
},[])
console.log(unique)//[1, 2, 3, 4]
//Find sum using reduce
var sum = (a, b) => a+b
var data = arrB.reduce((result, curr) => {
if(!result[curr]) result[curr] = []
result[curr].push(curr);
return result
},{})
var arrSum = Object.keys(data).map(key => data[key].reduce(sum).toFixed(1))
console.log(arrSum) //[0.4,0.6,0.6,0.8]

One-Dimensional Array Iteration Using Recursion

I'm trying to iterate over a simple array using recursion. For this specific case, I'm trying to recreate .map() using recursion (without using .map()!. I currently am only pushing the last element in the original array, but I want to push all into the array.
function recursiveMap (arr, func) {
let newArr = [];
if (arr.length === 1){
newArr.push(func(arr));
}
else {
newArr.push(...recursiveMap(arr.slice(1),func));
}
return newArr;
}
You need to to use func on the current item, and spread the result of calling the function on the rest of the array:
function recursiveMap(arr, func) {
return arr.length ? [func(arr[0]), ...recursiveMap(arr.slice(1), func)] : [];
}
const arr = [1, 2, 3];
const result = recursiveMap(arr, n => n * 2);
console.log(result);
Your base case seems wrong. You will need to check for an empty array:
function recursiveMap (arr, func) {
let newArr = [];
if (arr.length === 0) {
// do nothing
} else {
newArr.push(func(arr[0]));
newArr.push(...recursiveMap(arr.slice(1),func));
}
return newArr;
}
Instead you will need to call func (on the first item) when there is at least one element.
With recursion, I find it is helpful to have the base case be the very first thing you check in your function, and short the execution there. The base case for map is if the array has 0 items, in which case you would return an empty array.
if you haven't seen it before let [a, ...b] is array destructuring and a becomes the first value with b holding the remaining array. You could do the same with slice.
function recursiveMap(arr, func){
if(arr.length == 0) return [];
let [first, ...rest] = arr;
return [func(first)].concat(recursiveMap(rest, func));
}
let test = [1,2,3,4,5,6,7];
console.log(recursiveMap(test, (item) => item * 2));
EDIT
Going back to your sample I see you clearly have seen destructuring before xD, sorry. Leaving it in the answer for future readers of the answer though.
Below are a few alternatives. Each recursiveMap
does not mutate input
produces a new array as output
produces a valid result when an empty input is given, []
uses a single pure, functional expression
Destructuring assignment
const identity = x =>
x
const recursiveMap = (f = identity, [ x, ...xs ]) =>
x === undefined
? []
: [ f (x), ...recursiveMap (f, xs) ]
const square = (x = 0) =>
x * x
console.log (recursiveMap (square, [ 1, 2, 3, 4, 5 ]))
// [ 1, 4, 9, 16, 25 ]
Array slice
const identity = x =>
x
const recursiveMap = (f = identity, xs = []) =>
xs.length === 0
? []
: [ f (xs[0]), ...recursiveMap (f, xs.slice (1)) ]
const square = (x = 0) =>
x * x
console.log (recursiveMap (square, [ 1, 2, 3, 4, 5 ]))
// [ 1, 4, 9, 16, 25 ]
Additional parameter with default assignment – creates fewer intermediate values
const identity = x =>
x
const recursiveMap = (f = identity, xs = [], i = 0) =>
i >= xs.length
? []
: [ f (xs[i]) ] .concat (recursiveMap (f, xs, i + 1))
const square = (x = 0) =>
x * x
console.log (recursiveMap (square, [ 1, 2, 3, 4, 5 ]))
// [ 1, 4, 9, 16, 25 ]
Tail recursive (and cute)
const identity = x =>
x
const prepend = x => xs =>
[ x ] .concat (xs)
const compose = (f, g) =>
x => f (g (x))
const recursiveMap = (f = identity, [ x, ...xs ], then = identity) =>
x === undefined
? then ([])
: recursiveMap
( f
, xs
, compose (then, prepend (f (x)))
)
const square = (x = 0) =>
x * x
console.log (recursiveMap (square, [ 1, 2, 3, 4, 5 ]))
// [ 1, 4, 9, 16, 25 ]
// => undefined
recursiveMap (square, [ 1, 2, 3, 4, 5 ], console.log)
// [ 1, 4, 9, 16, 25 ]
// => undefined
recursiveMap (square, [ 1, 2, 3, 4, 5 ])
// => [ 1, 4, 9, 16, 25 ]
Derived from tail-recursive foldl – Note foldl chooses a similar technique used above: additional parameter with default assignment.
const identity = x =>
x
const foldl = (f = identity, acc = null, xs = [], i = 0) =>
i >= xs.length
? acc
: foldl
( f
, f (acc, xs[i])
, xs
, i + 1
)
const recursiveMap = (f = identity, xs = []) =>
foldl
( (acc, x) => acc .concat ([ f (x) ])
, []
, xs
)
const square = (x = 0) =>
x * x
console.log (recursiveMap (square, [ 1, 2, 3, 4, 5 ]))
// [ 1, 4, 9, 16, 25 ]
You could take another approach by using a third parameter for the collected values.
function recursiveMap(array, fn, result = []) {
if (!array.length) {
return result;
}
result.push(fn(array[0]));
return recursiveMap(array.slice(1), fn, result);
}
console.log(recursiveMap([1, 2, 3, 4, 5], x => x << 1));
console.log(recursiveMap([], x => x << 1));
welcome to Stack Overflow. You could either pass result to itself like in the following example:
function recursiveMap (arr, func,result=[]) {
if (arr.length === 0){
return result;
}
return recursiveMap(
arr.slice(1),
func,
result.concat([func(arr[0])])
);
}
console.log(recursiveMap([1,2,3,4],x=>(x===3)?['hello','world']:x+2));
Or define a recursive function in your function:
function recursiveMap (arr, func) {
const recur = (arr, func,result=[])=>
(arr.length === 0)
? result
: recur(
arr.slice(1),
func,
result.concat([func(arr[0])])
);
return recur(arr,func,[])
}
console.log(recursiveMap([1,2,3,4],x=>(x===3)?['hello','world']:x+2));
Add newArr.push(func(arr[0])); before calling again the function
function recursiveMap (arr, func) {
let newArr = [];
if (arr.length === 1){
newArr.push(func(arr));
}
else {
newArr.push(func(arr[0]));
newArr.push(...recursiveMap(arr.slice(1),func));
}
return newArr;
}
console.log(recursiveMap([1,2,3], function(a){return +a+2}))
Same but modified answer with bugs corrected
function recursiveMap (arr, func) {
let newArr = [];
if(arr.length){
newArr.push(func(arr[0]));
if(arr.length > 1){
newArr.push(...recursiveMap(arr.slice(1),func));
}
}
return newArr;
}
console.log(recursiveMap([1,2,3], function(a){return a+2}))

How i can substract and sum results all arrays in array

I have a few arrays in array :
([[10,0],[3,5],[5,8]])
I try substract all inner arrays a - b and then sum results ( example : 10 - 0 = 10, 3-5 = -2, 5-8 = -3, 10+(-2)+(-3) = 5;
My try:
var el;
return array.reduce((a, b) => a - b );
But my result came out Nan, now Im understood, in my code i want substring array from array - bad idea.
I know how do this with using for or something like that, my question is:
how i can do this with use reduce or other ''modern'' method?
Thanks for help.
PS sorry for my English skill ;)
You can use reduce() method like this.
var data = [[10,0],[3,5],[5,8]]
var result = data.reduce((r, e) => r + (e[0] - e[1]), 0);
console.log(result)
Flexible solution, the size of the nested arrays doesn't matter, it will still return a proper result.
const count = (arr) => arr.reduce((s, v) => {
s += v.reduce((a,b) => a - b);
return s;
}, 0);
let arr1 = [ [10, 0], [3, 5], [5, 8] ],
arr2 = [ [5, 4, 1], [3, 5, 5], [5, 8] ];
console.log(count(arr1));
console.log(count(arr2));
Something like this? You were close, but be sure to have an initial value of 0 and dereference the inner arrays into a and b like so:
var array = [[10,0],[3,5],[5,8]];
var result = array.reduce((prev, [a, b]) => prev + (a - b), 0);
console.log(result);
const arr = ([[10,8],[3,5],[5,8]]);
arr.map(pair => pair[0] - pair[1]).reduce((a,b) => a + b)
You could reduce the outer and inner arrays.
var array = [[10, 0], [3, 5], [5, 8]],
result = array.reduce(function (r, a) {
return r + a.reduce(function (x, y) {
return x - y;
})
}, 0);
console.log(result);

Average of bidimensional array's columns with array.map()

I have an array that looks like this:
var array = [ [1,3,9],
[4,6,8],
[3,7,5],
[2,8,4] ];
I want to get the average number of each column, but for now I was just trying to sum them. This is my code:
var sum = function(arr) {
return arr.reduce(function(a, b) { return a + b; }, 0);
};
var tests = array.map(function(v, i) {
return sum(array.map(function(v) { return v[i]; }))
});
return tests;
The output turns the sum correctly, but it seems to be doing as many sums as there are rows (4 rows), instead of 3 corresponding to the columns. This is the output:
tests = [10, 24, 26, NULL]
Any idea why is this happening? How can I perform the calculation only for as many columns as there are instead of rows?
EDIT:
I'm using Nenad's answer which gives the correct result. But I need to implement it on Google Sheets's Script Editor, which doesn't seem to understand the shortened functions with "=>". I replaced the shortened pieces for the longer version, but I'm not getting the same result.
var array = [ [1,3,9],
[4,6,8],
[3,7,5],
[2,8,4] ];
var sums = array.reduce(function(r, e, i) {
e.forEach(function(a,j) { r[j] = (r[j] || 0) + a;
if (i == array.length-1) { r = r.map(function(el){ return el/array.length; }); }
});
return r;
}, [])
console.log(sums);
I don't see any difference between this and the shortened version, yet this one returns:
sums = [0.15625, 0.75, 1.34375];
Instead of:
sums = [2.5, 6, 6.5];
The sum is done correctly, but when I divide "el/array.length" or even "el/4", the result are these 3 weird numbers. I don't understand where are those coming from. Where did I go wrong?
You can use reduce() and forEach() to return result.
var array = [
[1, 3, 9],
[4, 6, 8],
[3, 7, 5],
[2, 8, 4]
];
var sums = array.reduce(function(r, e, i) {
e.forEach((a, j) => r[j] = (r[j] || 0) + a)
return r;
}, [])
console.log(sums)
To calculate avg for each column you can add map() on last iteration of array.
var array = [
[1, 3, 9],
[4, 6, 8],
[3, 7, 5],
[2, 8, 4]
];
var sums = array.reduce(function(r, e, i) {
e.forEach((a, j) => r[j] = (r[j] || 0) + a)
if (i == array.length - 1) r = r.map(el => el / array.length);
return r;
}, [])
console.log(sums)
To get the average of each column you'd first have to know the amount of columns. Then grab each column with map() and to sum everything with reduce()
Now we have the column, the sum and then just divide by column length.
const arrayColumn = (a, n) => a.map(x => x[n]);
const arraySum = (a) => a.reduce((b,c) => b + c);
var arr = [
[1,3,9],
[4,6,8],
[3,7,5],
[2,8,4]
];
for(i=0; i<arr[0].length; i++){
console.log(arraySum((col = arrayColumn(arr, i))) / col.length);
}
This will convert the 2d array of rows into a 2d array of columns and then maps each inner array of columns to an average. There is a little bit of boilerplate to make the inner reduce immutable you could use lodash/fp or another library to clean this up.
const array = [
[1,3,9],
[4,6,8],
[3,7,5],
[2,8,4]
];
const averageColumns = array => array.reduce((acc, row) => {
return row.reduce((accRow, col, index) => {
const cols = accRow[index] || [];
return [...accRow.slice(0, index), cols.concat(col), ...accRow.slice(index + 1)];
}, acc);
}, []).map(avg);
const avg = array => array.reduce((acc, next) => acc + next, 0) / array.length;
console.log(averageColumns(array));
map() + reduce() solution
var array = [ [1,3,9], [4,6,8], [3,7,5], [2,8,4] ];
array.map(function(item, i, arr) {
arr[i] = item.reduce((a, b) => a + b, 0) / 2;
console.log(arr[i])
});
I'm a little fix up your code
you have mistake here return sum(array.map(function(v) { return v[i]; }))
var array = [ [1,3,9],
[4,6,8],
[3,7,5],
[2,8,4] ];
function sum(arr) {
return arr.reduce(function(a, b) { return a + b; }, 0);
};
var tests = array.map(function(v, i, arr) {
return sum(arr[i])
});
tests;
You can transpose the array of arrays,
popular utility libraries (ramda for ex) have a transpose implementation,
but it's easy to implement your own:
const trans = arr => arr[0].map((_,i) => arr.map(x => x[i]))
var array = [
[1,3,9],
[4,6,8],
[3,7,5],
[2,8,4]
];
const res = trans(array)
console.log(res)
// to get the sum you can use reduce
console.log(res.map( x => x.reduce((a,b) => a + b )))
I would do as follows;
var array = [ [1,3,9],
[4,6,8],
[3,7,5],
[2,8,4] ];
result = array.map(a => a.reduce((p,c,_,a) => p + c/a.length,0));
console.log(result);
As per the comments... Yes they are right, the right solution should be through a switch of the .map() and .reduce();
var array = [ [1,3,9],
[4,6,8],
[3,7,5],
[2,8,4] ],
result = array.reduce((p,c) => p.map((n,i) => n + c[i]/array.length), Array(array[0].length).fill(0));
console.log(result);

Sequentially Pairing Items in an Array

Given an array, [1, 2, 3, 4, 5], what is the most efficient method for pairing up each of the items sequentially, like so: [[1,2], [2,3], [3,4], [4,5]]?
I've been trying to use the reduce method but to no avail and want something elegant.
Use simple for loop
var data = [1, 2, 3, 4, 5];
var res = [];
for (var i = 0; i < data.length-1; i++) {
res.push(data.slice(i, i+2));
}
console.log(res);
With Array#reduce method
console.log(
[1, 2, 3, 4, 5].reduce(function(a, b, i) {
if (i == 1) return [[a, b]];
a.push([a[a.length - 1][1], b]);
return a
})
)
With Array#reduce method with initial value as empty array
console.log(
[1, 2, 3, 4, 5].reduce(function(a, b, i, arr) {
arr[i + 1] !== undefined && a.push([b, arr[i + 1]])
return a
}, [])
)
To answer the "elegant" bit... ;)
let pairwise = function *(it) {
var
a = it[Symbol.iterator](),
b = it[Symbol.iterator]();
b.next();
for (var x of b) {
yield [a.next().value, x]
}
};
console.log(Array.from(pairwise([1,2,3,4,5])))
Using lodash for given array:
var result = _.chunk( _.sortBy(array.concat(_.slice(array, 1, array.length - 1))), 2);
Check jsfiddle
So if array = [1,2,3,4,5] we have steps:
_.slice(array, 1, array.length - 1)
// = [2,3,4]
array.concat(_.slice(array, 1, array.length - 1)
// = [1,2,3,4,5].concat([2,3,4]) = [1,2,3,4,5,2,3,4]
_.sortBy(array.concat(_.slice(array, 1, array.length - 1))
// _sortBy([1,2,3,4,5,2,3,4]) = [1,2,2,3,3,4,4,5]
_.chunk( _.sortBy(array.concat(_.slice(array, 1, array.length - 1))), 2)
// _chunk([1,2,2,3,3,4,4,5],2) = [[1,2],[2,3],[3,4],[4,5]]
Another short solution using Array.forEach and Array.push functions:
var arr = [1, 2, 3, 4, 5], pairs = [];
arr.forEach((v, k, arr) => arr[k+1] && pairs.push([v, arr[k+1]]));
console.log(JSON.stringify(pairs)); // [[1,2],[2,3],[3,4],[4,5]]
Using reduce:
const res = [1, 2, 3, 4, 5].reduce(
([b, acc], a) => [a, acc.concat([[b, a]])]
, [null, []])[1].slice(1)
console.log(res)
The seed of reduce is a tuple of two items: [null, []]. null represents the current element in the array and [] is the result.
In the first iteration of reduce:
([b, acc], a) => ... b = null and acc = []
The function produces a new tuple, the first item in the tuple is the current element of the array and the second item is the result. In the second iteration:
([b, acc], a) => ..., b = 1 and acc = [[null, 1]]
The second iteration will add (concat) [1, 2] to the result (acc).
In the third iteration:
([b, acc], a) => ..., b = 2 and acc = [[null, 1], [1, 2]]
And so on so forth:
const trace = (x, y) => {
console.log(x);
return y;
}
const json = x => JSON.stringify(x)
const res = [1, 2, 3, 4, 5].reduce(
([b, acc], a) => trace(
`a = ${a}, b = ${b} acc = ${json(acc)} ++ ${json([[b, a]])}`
, [a, acc.concat([[b, a]])]
)
, [null, []]) // seed
// the result is a tuple `[currentElement, finalResult], we extract finalResult here
[1]
// -1 element of the original array was null (check the seed), let's remove it from the result
.slice(1)
console.log(res)
We can think about the problem another way: we are kind of joining the elements of the same array with each other into tuples. Using Ramda zip function is elegant but has a performance tradeoff because we go thru the list twice:
const arr = [1, 2, 3, 4, 5]
const res = R.zip(arr, R.drop(1, arr))
console.log(res)
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.21.0/ramda.min.js"></script>
Reduce is most elegant way to do that.
[1,2,3,4,5].reduce((a,b,c) => {
a.push([c,b]);
return a;
}, [])

Categories