summing multiple arrays using one index as key - javascript

I am importing data from multiple csv files, and the last one needs a little mapping and reducing.
Promise.all([
d3.csv("x.csv"),
d3.csv("y.csv"),
d3.csv("z.csv")
]).then(function(data) {
var alpha = data.map(row=>[row.key, row.item1, row.item2]);
var beta = data.filter(row=> (row.key=='x'))
.map(row=> [row.key, row.item1, row.item2]);
var gamma = data.filter(row=> (row.key=='x') ||
(row.key=='y'))
.map(row=>[row.key, row.item1, row.item2]);
});
Now I have to first convert all number strings to integers as well as perform a reduction:
I have multiple arrays in JavaScript:
[[x, 5, 4],
[x, 5, 6],
[y, 3, 9],
[y, 4, 6]]
which I want to reduce/sum based on index[0] as the key and using reduce function specifically.
So the result:
[[x,10,10],
[y,7,15]]
I am confused as to how do I select a particular index as a key in summation process.
.map(row=>[row.key, row.item1, row.item2]) //produces the array mentioned above
.reduce(row=>[row.item1, row.item2]); //to sum up every row?
I think my mistake is on the reduce part.

i hope this helps
var arr = [['a', 5, 4],
['a', 5, 6],
['b', 3, 9],
['b', 4, 6]];
const newArr = arr.reduce((acc, cur) => {
const prev = acc.find(elem => elem[0] === cur[0]);
if(prev) {
prev[1] += cur[1];
prev[2] += cur[2];
}
else {
acc.push(cur);
}
return acc;
}
, []);
console.log(newArr);

Related

How to rearrange 3 arrays with according to the elements and return as one array?

I have 3 arrays. For example, given the arrays are
arr1 = [2, 7]
arr2 = [0, 1, 16]
arr3 = [3, 6, 9]
And I would like to rearrange them according to the numbers and output as an array.
result = ['arr2', 'arr2', 'arr1', 'arr3', 'arr3', 'arr1', 'arr3', 'arr2']
I think it might have something to do with looping, but I've no luck after struggling for a while. Is there any way to get the expected result?
You could move the values to an object and take another object for the indices and sort an array of key.
Then take the key at index zero and go on until the index is equal to the length of the array, then stop the iteration.
const
arr1 = [2, 7],
arr2 = [0, 1, 16],
arr3 = [3, 6, 9],
values = { arr1, arr2, arr3 },
result = [],
keys = Object.keys(values),
indices = Object.fromEntries(keys.map(k => [k, 0]));
while (true) {
keys.sort((a, b) => (values[a][indices[a]] ?? Number.MAX_VALUE) - (values[b][indices[b]] ?? Number.MAX_VALUE));
if (indices[keys[0]] === values[keys[0]].length) break;
result.push(keys[0]);
indices[keys[0]]++;
}
console.log(...result);
A shorter approach by mapping entries, sorting and mapping again.
const
arr1 = [2, 7],
arr2 = [0, 1, 16],
arr3 = [3, 6, 9],
result = Object
.entries({ arr1, arr2, arr3 })
.flatMap(([k, a]) => a.map(v => [k, v]))
.sort(([, a], [, b]) => a - b)
.map(([k]) => k);
console.log(...result);
Option 1. Allocate an array whose size is equal to the total count of elements in the 3 arrays. Populate the newly created array with the elements from your 3 small arrays. Then sort the created array.
Option 2. Merge 2 of the 3 arrays to produce a sorted array with elements from the chosen 2 small arrays. Then merged the sorted array from the previous step with the 3rd array to get the array that you need.
Not an elegant solution but maybe something like can help
arr1 = [2, 7]
arr2 = [0, 1, 16]
arr3 = [3, 6, 9]
let concatedArray = [...arr1, ...arr2, ...arr3].sort((a, b) => a - b);
let finalArr = []
concatedArray.forEach(val => {
let doesNumberExist = false;
let arrName = ''
doesNumberExist = arr1.includes(val);
arrName = 'arr1';
if (!doesNumberExist) {
doesNumberExist = arr2.includes(val);
arrName = 'arr2'
}
if (!doesNumberExist) {
doesNumberExist = arr3.includes(val);
arrName = 'arr3'
}
finalArr.push(arrName);
}
)
console.log(finalArr)

Alternating values from two arrays into a new third array

I'm on my third week of learning Javascript and got an assignment that's giving me some trouble.
I'm supposed to create a function called mix that takes two parameters that are two arrays. When called the function should return a new list which alternates between the two previous arrays (see example below).
This is about arrays and loops so I need to use those. also, I'm only allowed to use the array functions: push, pop, shift & unshift.
My teacher said that this is solved the easiest using a while loop.
Example
mix([], []) === []
mix([2, 4], []) === [2, 4]
mix([], [8, 16]) === [8, 16]
mix([1, 3, 5], [2, 4]) === [1, 2, 3, 4, 5]
mix([10, 9], ['a', 'b', 'c']) === [10, 'a', 9, 'b', 'c']
Before I got the tip about the easiest being a while loop I started trying with a for a loop. The problem I'm having here is that it works as long as the arrays are the same length, but I'm having trouble understanding how I can solve it so the arrays can have different lengths.
Since I'm trying to learn I want pointers in the right direction and not the whole answer!
Please excuse my chaotic beginner code :)
My current code
function mix(array1, array2) {
let newList = [];
for(i = 0; i < array1.length || i < array2.length; i++) {
if(array1.length > 0 || array2.length > 0){
newList.push( array1[i] );
newList.push( array2[i] );
}
}
return newList;
}
mix([10, 9],['a', 'b', 'c'])
I would also like a pointer for how a while loop would be easier and how i would go about using that instead.
Thanks in advance!
To fix your current code, you need to separately check whether i < array1.length (and if so, push array1[i]), and also do the same sort of test for array2:
function mix(array1, array2) {
let newList = [];
for (let i = 0; i < array1.length || i < array2.length; i++) {
if (i < array1.length) {
newList.push(array1[i]);
}
if (i < array2.length) {
newList.push(array2[i]);
}
}
return newList;
}
console.log(mix([10, 9], ['a', 'b', 'c']));
Make sure to declare the i with let i, else you'll implicitly create a global variable (or throw an error in strict mode).
To do this with a while loop, I'd loop while either array has a length, and shift (remove the [0]th item) from them:
function mix(array1, array2) {
const newList = [];
while (array1.length || array2.length) {
if (array1.length) {
newList.push(array1.shift());
}
if (array2.length) {
newList.push(array2.shift());
}
}
return newList;
}
console.log(mix([10, 9], ['a', 'b', 'c']));
You can do much better with array shift, it takes first element from array and returns its value, so for example
const firstElement = [1, 2, 4].shift();
// firstElement - 1
// array [2, 4]
with this info you can now write your function like so:
function (arr1, arr2) {
const resultArr = [];
while(arr1.length && arr2.length) {
resultArr.push(arr1.shift());
resultArr.push(arr2.shift());
}
return resultArr.concat(arr1, arr2);
}
You can achieve it using Array.prototype.shift(), Array.prototype.push() and Spread syntax
function mix(arr1,arr2) {
var newArr=[];
while(arr1.length>0&&arr2.length>0) {
newArr.push(arr1.shift(),arr2.shift());
}
newArr.push(...arr1,...arr2);
return newArr;
}
An alternative approach could be to consider the input arrays as a two-dimensional array.
You can then:
rotate/transpose the two-dimensional array (rows become columns); and
flatten the result (rows are concatenated into a one-dimensional array).
The transformation looks like this for the example input [1, 3, 5], [2, 4]:
Rotate Flatten
[1, 3, 5], ⇒ [1, 2], ⇒ [1, 2, 3, 4, 5]
[2, 4] [3, 4],
[5]
Or, in code:
const mix = (...arrays) => {
const transposed = arrays.reduce((result, row) => {
row.forEach((value, i) => result[i] = [...result[i] || [], value]);
return result;
}, []);
return transposed.flat();
};
console.log(mix([], [])); // === []
console.log(mix([2, 4], [])); // === [2, 4]
console.log(mix([], [8, 16])); // === [8, 16]
console.log(mix([1, 3, 5], [2, 4])); // === [1, 2, 3, 4, 5]
console.log(mix([10, 9], ['a', 'b', 'c'])); // === [10, 'a', 9, 'b', 'c']
Benefits of this approach are that it automatically scales to allow more than two input arrays, and, unlike the shift operations, does not mutate the input arrays.

How to get distinct values from an array of arrays in JavaScript using the filter() method? [duplicate]

This question already has answers here:
How to remove duplicates from a two-dimensional array? [closed]
(3 answers)
Closed 3 years ago.
I have an array like this:
let x = [[1, 2], [3, 4], [1, 2], [2, 1]];
What should I do to retrieve an array without the duplicates?
[[1, 2], [3, 4], [2, 1]];
I would like to use the filter method. I tried this but it doesn't work:
x.filter((value,index,self) => (self.indexOf(value) === index))
EDIT: as I specified to use the filter method, I don't think this question is a duplicate. Also, I got several interesting answers.
Try converting the inner arrays to a string, then filter the dupes and parse the string again.
let x = [[1, 2], [3, 4], [1, 2]];
var unique = x.map(ar=>JSON.stringify(ar))
.filter((itm, idx, arr) => arr.indexOf(itm) === idx)
.map(str=>JSON.parse(str));
console.log(unique);
Filter just causes things to get into O(n^2).
The currently accepted answer uses .filter((itm, idx, arr) => arr.indexOf(itm) === idx) which will cause the array to be iterated each time during each iteration... n^2.
Why even go there? Not only that, you need to parse in the end. It is a lot of excess.
There is no real good way to use filter without hitting O(n^2) here, so if performance is the goal is should probably be avoided.
Instead, just use reduce. It is very straightforward and fast easily accomplishing O(n).
"Bin reduce the set to unique values."
let x = [[1, 2], [3, 4], [1, 2], [2, 1]];
let y = Object.values(x.reduce((p,c) => (p[JSON.stringify(c)] = c,p),{}));
console.log(y);
In case it isn't as clear, here is a more readable version of the bin reduction.
// Sample Data
let dataset = [[1, 2], [3, 4], [1, 2], [2, 1]];
// Create a set of bins by iterating the dataset, which
// is an array of arrays, and structure the bins as
// key: stringified version of the array
// value: actual array
let bins = {};
// Iteration
for(let index = 0; index < dataset.length; index++){
// The current array, from the array of arrays
let currentArray = dataset[index];
// The JSON stringified version of the current array
let stringified = JSON.stringify(currentArray);
// Use the stringified version of the array as the key in the bin,
// and set that key's value as the current array
bins[stringified] = currentArray;
}
// Since the bin keys will be unique, so will their associated values.
// Discard the stringified keys, and only take the set of arrays to
// get the resulting unique set.
let results = Object.values(bins);
console.log(results);
If you were to have to go the route of filter, then n^2 must be used. You can iterate each item looking for existence using every.
"Keep every element which does not have a previous duplicate."
let x = [
[1, 2],
[3, 4],
[1, 2],
[2, 1]
];
let y = x.filter((lx, li) =>
x.every((rx, ri) =>
rx == lx ||
(JSON.stringify(lx) != JSON.stringify(rx) || li < ri))
);
console.log(y);
Okay, the string hash idea is brilliant. Props to I wrestled a bear once. I think the code itself could be a bit better though, so here's how I tend to do this type of thing:
let x = [[1, 2], [3, 4], [1, 2]];
const map = new Map();
x.forEach((item) => map.set(item.join(), item));
console.log(Array.from(map.values()));
And if you want an ugly one liner:
let x = [[1, 2], [3, 4], [1, 2]];
const noRepeats = Array.from((new Map(x.map((item) => [item.join(), item]))).values());
console.log(noRepeats);
This is a solution with time complexity of O(n) where n is the number of elements in your array.
Using the filter method as the OP wants it:
const x = [[1, 2], [3, 4], [1, 2], [2, 1]];
const s = new Set();
const res = x.filter(el => {
if(!s.has(el.join(""))) {
s.add(el.join(""));
return true;
}
return false
})
console.log(res)
My personal preference here is to use ForEach as it looks more readable.
const x = [[1, 2], [3, 4], [1, 2], [2, 1]];
const s = new Set();
const res = [];
x.forEach(el => {
if(!s.has(el.join(""))) {
s.add(el.join(""));
res.push(el)
}
})
console.log(res);
We are using a Set and a simple combination of the elements of the array to make sure they are unique. Otherwise this would become O(n^2).
The equivalent to
x.filter((value,index,self) => (self.indexOf(value) === index))
would be
x.filter((v,i,self) => {
for1:
for (let j = 0; j < self.length; j++) {
if (i == j) {
return true;
}
if (self[j].length != v.length) {
continue;
}
for (let k = 0; k < v.length; k++) {
if (self[j][k] != v[k]) {
continue for1;
}
}
return false;
}
return true;
})
Unlike some of the other answers, this does not require a conversion to string and can thus work with more complex values.
Use === instead of == if you want.
The time complexity is not great, of course.
indexOf does not work on identical instances of arrays/objects type elements within an array, as such arrays just hold references.
In filter function instance you get via parameter v (in below code) is not the same instance as stored in array, making indexOf unable to return the index of it.
In below code, by converting objects to strings we can use indexOf to find duplicates.
let x = [[1, 2], [3, 4], [1, 2], [2, 1]];
console.log(x.
map(function(v){
return JSON.stringify(v)
})
.filter(function(v, i, o) {
return o.length == i ? true : o.slice(i + 1).indexOf(v) == -1;
})
.map(function(v) {
return JSON.parse(v)
})
);

algorithm of counting Overlaps of ranges in javascript

input integer is always positive
input: [[1, 4], [3, 7], [6, 8], [10,15]]
output: [[1, 8], [10,15]]
input: [[3, 4], [1,3], [5, 9], [5, 12]]
output: [[1, 4], [5, 12]]
I read this stackoverflow, but wandering if there is a better way.
You could sort the data ascending and then check the predecessor if it fits into the last range. If not append the actual array to the result set.
function groupRanges(array) {
return array
.sort(function (a, b) { return a[0]- b[0] || a[1]- b[1]; })
.reduce(function (r, a) {
var last = r[r.length - 1] || [];
if (a[0] <= last[1]) {
if (last[1] < a[1]) {
last[1] = a[1];
}
return r;
}
return r.concat([a]);
}, []);
}
console.log(groupRanges([[1, 4], [3, 7], [6, 8], [10,15]]));
console.log(groupRanges([[3, 4], [1,3], [5, 9], [5, 12]]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
For every start and end of range make a pair containing value and +1/-1 for start and end
Sort these pairs by value, using +-1 as secondary key in compare function: (x,+1) before (the same x,-1)
Make ActiveRanges=0, walk through pair list/array, adding +-1 to ActiveRanges.
When ActiveRanges becomes nonzero, big range begins.
When ActiveRanges becomes zero, big range finishes.
Here's what I came up with, usint the MathJS library to generate ranges :
let input = [[1, 4], [3, 7], [6, 8], [10,15]]
let workArray = input.map( arr => math.range(arr.join(":"), true )._data )
workArray = [].concat.apply([], workArray) // Concatenating all values
workArray = [...new Set(workArray)] // Deduplicating, sorting
let output = [],
min = workArray[0],
max = min
for( let i=0, l=workArray.length; i<l ; i++){
if(max+1 != workArray[i+1]){
output.push([min,max])
min = workArray[i+1]
max=min
} else {
max++
}
}
console.log(output) // [ [1,8] , [10,15] ]
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.13.1/math.min.js"></script>
If the array is of length 4 (as per your example) and everything is constant as you described it, here is a simple function you can use:
function reGroup(arr) {
const x = [arr[0][0], arr[2][1]];
return [x, arr[3]];
}

Adding pairs of numbers in array with reduce (javascript)

Given a 2D array, I want to add the last number of the preceeding inner array to the first number of the next inner array.
I managed to get up to the point where:
var output= [[1,2,3],[4,5,6],[7,8,9],[1,2,3]] //this becomes...
output = [3,4,6,7,9,1] (edited typo)
I now would like to add the pairs up to return this array:
output = [9, 11, 10]
So far, this is what I have, and it returns [3,6,4,7,9,1]. I would like to see how reduced can be used for this, but also interested in how a for loop would accomplish the same thing.
var output= [[1,2,3],[4,5,6],[7,8,9],[1,2,3]]
output = output
.reduce((newArr,currArr)=>{
newArr.push(currArr[0],currArr[currArr.length-1]) //[1,3,4,6,7,9]
return newArr
},[])
output.shift()
output.pop()
return output
Can use index argument of reduce
let output= [[1,2,3],[4,5,6],[7,8,9],[1,2,3]];
output = output
.reduce((newArr, currArr, i, origArr) => {
if (i > 0) {
let prevArr = origArr[i - 1];
newArr.push(currArr[0] + prevArr[prevArr.length - 1]);
}
return newArr
}, [])
console.log(output)
Not clear what should occur at last element of input array? You can use for..of loop, Array.prototype.entries() to sum values of specific index of arrays.
let output = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[1, 2, 3]
];
let res = Array.from({
length: output.length - 1
});
for (let [key, value] of output.entries()) {
if (key < output.length - 1)
res[key] = value[value.length - 1] + output[key + 1][0]
}
console.log(res)
You could do something like this, using reduce.
var input = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[1, 2, 3]
];
let output = [];
input.reduce((prevArr, currentArray) => {
if (prevArr) {
output.push(prevArr[prevArr.length - 1] + currentArray[0]);
}
return currentArray;
});
console.log(output);

Categories

Resources