I have an array like this:
[
["01/07", 1, 13],
["02/07", 0, 16],
["03/07", 0, 5],
["01/07", 2, 0],
["02/07", 2, 0],
["03/07", 12, 0],
["04/07", 12, 0],
["05/07", 9, 0]
]
I would like to join the rows when there are 2 equal dates and sum the 2 numeric values,
using the example above the result would be:
[
["01/07", 3, 13],
["02/07", 2, 16],
["03/07", 12, 5],
["04/07", 12, 0],
["05/07", 9, 0]
]
What would be the best possible way to achieve this?
Here is a solution using .reduce:
const joined = data.reduce((joined, [date, a, b]) => {
const match = joined.find(([dateB]) => dateB == date);
match ? (match[1] += a) && (match[2] += b) : joined.push([date, a, b]);
return joined;
}, []);
Here's a live example:
'use strict';
const data = [
["01/07", 1, 13],
["02/07", 0, 16],
["03/07", 0, 5],
["01/07", 2, 0],
["02/07", 2, 0],
["03/07", 12, 0],
["04/07", 12, 0],
["05/07", 9, 0]
];
const joined = data.reduce((joined, [date, a, b]) => {
const match = joined.find(([dateB]) => dateB == date);
match ? (match[1] += a) && (match[2] += b) : joined.push([date, a, b]);
return joined;
}, []);
console.log(joined);
const collaspsedArray = jsArray.reduce((acc, el) => {
const index = acc.findIndex(x => x[0] === el[0])
if (index === -1) {
acc.push(el)
return acc
}
for (let i = 1; i < acc[index].length; i++) {
acc[index][i] += el[i]
}
return acc
}, [])
I like this way
Use Array#reduce and a map to group entries by date. When adding to the map sum the numbers, then export the values with Object.values:
const unique_sum = xs =>
Object.values(
xs.reduce((acc, [date, a, b]) => {
const [, aa=0, bb=0] = acc[date] || [];
acc[date] = [date, a + aa, b + bb];
return acc;
}, {}));
console.log(unique_sum(data));
<script>
const data = [
["01/07", 1, 13],
["02/07", 0, 16],
["03/07", 0, 5],
["01/07", 2, 0],
["02/07", 2, 0],
["03/07", 12, 0],
["04/07", 12, 0],
["05/07", 9, 0]
];
</script>
Related
I'm using this function below to sum "columns" of a 2D Array, but some elements contain '-' and I haven't been able to handle it:
I've tried Number(num) or typeof num === 'number', but still...
const arr = [
['-', 2, 21],
[1, '-', 4, 54],
[5, 2, 2],
[11, 5, 3, 1]
];
const sumArray = (array) => {
const newArray = [];
array.forEach(sub => {
sub.forEach((num, index) => {
if(newArray[index]){
newArray[index] += num;
}else{
newArray[index] = num;
}
});
});
return newArray;
}
console.log(sumArray(arr))
You could use map and reduce to achieve this as well.
const arr = [
['-', 2, 21],
[1, '-', 4, 54],
[5, 2, 2],
[11, 5, 3, 1],
];
const sums = arr.map((sub) =>
sub.reduce((previous, current) => {
// check here that the value is a number
if (typeof current === 'number') {
return previous + current;
}
return previous;
}, 0)
);
console.log(sums);
// returns [23, 59, 9, 20]
Try:
const arr = [
['-', 2, 21],
[1, '-', 4, 54],
[5, 2, 2],
[11, 5, 3, 1]
];
const sumArray = (array) => {
const newArray = [];
array.forEach(sub => {
sub.forEach((num, index) => {
if (typeof num == 'number') {
if (newArray[index]) {
newArray[index] += num;
} else {
newArray[index] = num;
}
}
});
});
return newArray;
}
console.log(sumArray(arr))
Here's a more concise solution:
const arr = [
['-', 2, 21],
[1, '-', 4, 54],
[5, 2, 2],
[11, 5, 3, 1]
];
const result = arr.map((e, i) => arr.reduce((a, c) => (typeof c[i] == 'number' ? a + c[i] : a), 0))
console.log(result)
Using splice() would help remove the - from the array
For example, if your input array is [10, 5, 20], the output should be [2, 3, 1], since 10 is the second largest number, 5 is the third largest, and 20 is the largest.
This is my function:
function rankings(arr){
const result=[];
let newArr=arr.sort((a,b)=>b-a);
for (let i=0;i<arr.length;i++){
for (let j=0;j<newArr.length;j++){
arr[i]===newArr[j]? result.push(j+1): console.log('');
}
}
return(result);
}
Using my function with this array [10,5,20] as my input, my output is [1,2,3] while
rankings([10, 5, 20]); // [2, 3, 1] expected output
rankings([6, 8, 1, 12, 4, 3, 9]); // [4, 3, 7, 1, 5, 6, 2] expected output
You can also try to sort the array, and then match the indexes of the original array.
Edited to cater the case of repeated numbers
var originalArray = [10, 5, 20, 10, 20]
, setArray = [...new Set(originalArray)]
, sortedArray = [...setArray].sort((a, b) => a - b)
, dataIndex = originalArray.map(d => sortedArray.indexOf(d) + 1)
console.log(dataIndex)
You could sort the values and filter the items for avoiding same values with different ranks.
function rankings(array) {
const sorted = [...array]
.sort((a, b) => b - a)
.filter((b, i, { [i - 1]: a }) => a !== b);
return array.map(v => sorted.indexOf(v) + 1);
}
console.log(rankings([10, 5, 20])); // [2, 3, 1]
console.log(rankings([6, 8, 1, 12, 4, 3, 9])); // [4, 3, 7, 1, 5, 6, 2]
console.log(rankings([10, 5, 20, 10, 20]));
I want to make array that contain numbers into new array which has sum result of two by two.
For example, if there is number array like below
const arrayOne = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
I want to change it like below
const newArrayOne = [1, 5, 9, 13, 17]
other example is
const arrayTwo = [10, 11, 12, 13, 14, 15]
const newArrayTwo = [21, 25, 29]
How can I achieve this?
You could take flatMap and return every second sum.
var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
pairs = array.flatMap((v, i, a) => i % 2 ? [] : [v + (a[i + 1] || 0)]);
console.log(pairs);
You can simply do it with for iteration.
const arrayOne = [10, 11, 12, 13, 14, 15, 17]
const result = [];
for(var i = 0; i < arrayOne.length; i+=2){
const sum = arrayOne[i] + ((i + 1 < arrayOne.length) ? arrayOne[i + 1] : 0);
result.push(sum);
}
console.log(result)
let array = [ 1, 3, 5, 7, 9, 11]
let newArray = []
for (let i =0; i < array.length; i = i + 2){
newArray.push(array[i] + array[i+1])
}
console.log(newArray)
You can use reduce:
const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
const output = arr.reduce((a, e, i) => (a[a.length - 1] += e, i % 2 && i !== arr.length - 1 && a.push(0), a), [0])
console.log(output)
Or, you could chunk the array, then add the elements together:
const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
const output = arr.reduce((a, _, i) => (i % 2 === 0 && a.push(arr.slice(i, i + 2)), a), [])
.flatMap(([a,b]) => a + b)
console.log(output)
Having an array of numbers setOfNumbers = [0, 3, 3, 2, 7, 1, -2, 9] I'd like to sort this set to have the smallest numbers on the end and beginning and the biggest in the center of the sorted set like this sortedSetNumbers = [0, 2, 3, 9, 7, 3, 1, -2].
const setOfNumbers = [0, 3, 3, 2, 7, 1, -2, 9];
const result = [0, 2, 3, 9, 7, 3, 1, -2];
function sortNormal(a, b) {
return true; // Please, change this line
}
const sortedSetNumbers = setOfNumbers.sort((a, b) => sortNormal(a, b));
if (sortedSetNumbers === result) {
console.info('Succeeded Normal Distributed');
} else {
console.warn('Failed Normal Distribution');
}
console.log(sortedSetNumbers);
I am sure it is possible to sort these numbers with the method Array.prototype.sort(), but how should this sorting function look like?
EDIT: The solution does not have to be solved with .sort(). That was only an idea.
This might be the most naive way to do it, but isn't it simply left, right, left, right... after sorting?
const input = [0, 3, 3, 2, 7, 1, -2, 9];
const expected = [0, 2, 3, 9, 7, 3, 1, -2];
const sorted = input.slice().sort();
const output = [];
let side = true;
while (sorted.length) {
output[side ? 'unshift' : 'push'](sorted.pop());
side = !side;
}
console.log(expected.join());
console.log(output.join());
Or simply:
const input = [0, 3, 3, 2, 7, 1, -2, 9];
const output = input.slice().sort().reduceRight((acc, val, i) => {
return i % 2 === 0 ? [...acc, val] : [val, ...acc];
}, []);
console.log(output.join());
A slightly different approach is to sort the array ascending.
Get another array of the indices and sort the odds into the first half asending and the even values to the end descending with a inverted butterfly shuffle.
Then map the sorted array by taking the value of the sorted indices.
[-2, 0, 1, 2, 3, 3, 7, 9] // sorted array
[ 1, 3, 5, 7, 6, 4, 2, 0] // sorted indices
[ 0, 2, 3, 9, 7, 3, 1, -2] // rebuild sorted array
var array = [0, 3, 3, 2, 7, 1, -2, 9].sort((a, b) => a - b);
array = Array
.from(array, (_, i) => i)
.sort((a, b) => b % 2 - a % 2 || (a % 2 ? a - b : b - a))
.map(i => array[i]);
console.log(array);
This solution is not really elegant, but it does it's job.
const setOfNumbers = [0, 3, 3, 2, 7, 1, -2, 9];
const alternation = alternate();
const sortedSetNumbers = sortNormal(setOfNumbers);
function sortNormal(start) {
const result = [];
const interim = start.sort((a, b) => {
return b - a;
});
interim.map(n => {
if (alternation.next().value) {
result.splice(0, 0, n);
} else {
result.splice(result.length, 0, n);
}
});
return result;
}
function* alternate() {
let i = true;
while (true) {
yield i;
i = !i;
}
}
console.log(sortedSetNumbers);
Having a bi-dimensional array of this form:
arr = [
["12325-a", 1, 1, 1],
["43858-b", 3, 4, 1],
["84329-a", 6, 5, 2],
["18767-b", 0, 9, 0],
["65888-b", 5, 4, 4],
];
On each sub-array the first element is a string.
I want to combine together the sub-arrays having the same end. In this case it will be two groups : -a and -b.
The numerical values should be computed as sum based on idex.
So the result would look like:
arr = [
["-a", 7, 6, 3],
["-b", 8, 17, 5],
];
my solution (which does not work):
let arr = [
["12325-a", 1, 1, 1],
["43858-b", 3, 4, 1],
["84329-a", 6, 5, 2],
["18767-b", 0, 9, 0],
["65888-b", 5, 4, 4],
];
result = arr.reduce(function(acc, curr) {
if (acc[curr[0].substr(curr[0].length - 2)]) {
acc[curr[0]] = acc[curr[0]].map(function(val, index) {
if (index) {
return val + curr[index];
}
return val;
});
} else {
acc[curr[0]] = curr;
}
return acc;
}, {});
console.log(result)
You could first use reduce method to create an object and then Object.values to get an array of values.
const arr = [
["12325-a", 1, 1, 1],
["43858-b", 3, 4, 1],
["84329-a", 6, 5, 2],
["18767-b", 0, 9, 0],
["65888-b", 5, 4, 4],
];
const result = arr.reduce((r, [str, ...rest]) => {
let key = str.split(/(\d+)/).pop();
if(!r[key]) r[key] = [key, ...rest];
else rest.forEach((e, i) => r[key][i + 1] += e)
return r;
}, {})
console.log(Object.values(result))
You aren't using the correct key while checking for existing value and mapping over the existing data. Your solution would look like
let arr = [
["12325-a", 1, 1, 1],
["43858-b", 3, 4, 1],
["84329-a", 6, 5, 2],
["18767-b", 0, 9, 0],
["65888-b", 5, 4, 4],
];
result = arr.reduce(function(acc, curr) {
const key = curr[0].substr(curr[0].length - 2);
console.log(key)
if (acc[key]) {
acc[key] = acc[key].map(function(val, index) {
if (index) {
return val + curr[index];
}
return val;
});
} else {
acc[key] = [curr[0].substr(curr[0].length - 2), ...curr.slice(1)]
}
return acc;
}, {});
console.log(Object.values(result));
Use object to reduce to create an object where the last two characters of the first string will be the key. The value of that key will be an array which will contain the next next set of values.
In the second case if the object already have the key then get the index and sum the value with it.
Finally you can do Object.values to get an array
let arr = [
["12325-a", 1, 1, 1],
["43858-b", 3, 4, 1],
["84329-a", 6, 5, 2],
["18767-b", 0, 9, 0],
["65888-b", 5, 4, 4],
];
let x = arr.reduce(function(acc, curr) {
// getting last two characters from first string
let getSubstring = curr[0].slice(-2);
//checking if object has a key with this name.
// if not then create it
if (!acc.hasOwnProperty(getSubstring)) {
acc[getSubstring] = [];
// now iterate over the rest of the values and push them
for (let i = 1; i < curr.length; i++) {
acc[getSubstring].push(curr[i])
}
} else {
// if already a key exist then create an array of the elements except the first value
let newArray = curr.splice(1, curr.length);
newArray.forEach(function(item, index) {
acc[getSubstring][index] = acc[getSubstring][index] + item
})
}
return acc;
}, {});
for (let keys in x) {
x[keys].unshift(keys)
}
console.log(Object.values(x))