Related
If I have an array of numbers and I want to get the one that occurs the most frequent, yet there are two possible answers, Im having trouble sorting that part out. For example, below should return 1 and 7 but I only get 7. Any help is appreciated.
let arr = [1, 1, 2, 3, 4, 5, 6, 7, 7];
function findMode(numbers) {
let counted = numbers.reduce((acc, curr) => {
if (curr in acc) {
acc[curr]++;
} else {
acc[curr] = 1;
}
return acc;
}, {});
let mode = Object.keys(counted).reduce((a, b) => counted[a] > counted[b] ? a : b);
return mode;
}
console.log(findMode(arr));
You could group equal items in sub-arrays, then sort by sub-array length and retrieve the first values with the same array length like this:
const arr = [1, 1, 2, 3, 4, 5, 6, 7, 7],
output = arr
.sort((a, b) => a - b)
.reduce(
(acc, cur, i, { [i - 1]: last }) =>
(cur === last ? acc[acc.length - 1].push(cur) : acc.push([cur])) && acc,
[]
)
.sort((a, b) => b.length - a.length)
.reduce(
(a, b, _, { 0: first }) => (first.length === b.length ? [...a, b[0]] : a),
[]
);
console.log(output);
You can use an array as the accumulator.
let arr = [1, 1, 2, 3, 4, 5, 6, 7, 7];
function findMode(numbers) {
let counted = numbers.reduce((acc, curr) => {
if (curr in acc) {
acc[curr]++;
} else {
acc[curr] = 1;
}
return acc;
}, {});
let mode = Object.keys(counted).reduce((acc, curr) => {
if(!acc.length || counted[curr] > counted[acc[0]]) return [curr];
if(counted[curr] === counted[acc[0]]) acc.push(curr);
return acc;
}, []);
return mode;
}
console.log(findMode(arr));
Alternatively, you can find the highest frequency and then use filter to find numbers with that frequency.
let arr = [1, 1, 2, 3, 4, 5, 6, 7, 7];
function findMode(numbers) {
let counted = numbers.reduce((acc, curr) => {
if (curr in acc) {
acc[curr]++;
} else {
acc[curr] = 1;
}
return acc;
}, {});
let mode = Math.max(...Object.values(counted));
return Object.keys(counted).filter(x => counted[x] === mode);
}
console.log(findMode(arr));
You can keep track of the max number of occurrences in the first loop, and then use Array#filter to get the keys with such value:
function findMode(numbers) {
let max = 0;
const counted = numbers.reduce((acc, curr) => {
if (curr in acc) acc[curr]++;
else acc[curr] = 1;
if(acc[curr] > max) max = acc[curr];
return acc;
}, {});
const mode = Object.keys(counted)
.filter(key => counted[key] === max)
.map(Number);
return mode;
}
console.log( findMode([1, 1, 2, 3, 4, 5, 6, 7, 7]) );
As the whole action is happening within a function scope we can also do it with two .forEach() loops: in the first one we collect the counts and in the second one we then assemble a results array with the "winners".
By using a map for collecting the counts we avoid the type conversion to string that would have occurred had we used a plain object.
let arr = [1, 1, 2, "1", 3, 4, "1", 5, 6, 7, 7];
function findMode(nums) {
let cn=new Map(),mx=0,res;
nums.forEach(n=>cn.set(n,(cn.get(n)||0)+1));
[...cn.entries()].forEach(([v,c])=>{
if(c>mx) {res=[v];mx=c}
else if (c===mx) res.push(v) });
return res;
}
console.log(findMode(arr));
Given the array const vals = [1, 2, 3, 4, 5, 6, 7, 8, 9];
How can I filter and return a new array of indexed key/value pair objects for example:
const vals = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// My fail attempt using filter()
let obj = vals.filter((n, i) => {
return new Object({ i: n % 2 });
});
return obj;
// expected result [{1:2}, {3:4}, {5:6}, {7:8}]
I need to keep the index values as I will filter 2 different arrays with different criteria and associated them later.
Update
Second attempt using map() as suggested in the comments
let obj = vals.map((n, i) => {
if (n % 2) {
return { [i]: n };
}
});
Gives me the following:
[{0:1}, undefined, {2:3}, undefined, {4:5}, undefined, {6:7}, undefined, {8:9}]
To get a list of { key: value } objects where key is the index, and the values are only even without the odd values, you can do this:
const vals = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const result = vals.map((v, i) => [i, v])
.filter(([_, v]) => v % 2 == 0)
.map(([i, v]) => ({ [i]: v }));
console.log(result);
With the first map, you make a list of [[0, 1], ...] pairs to save the index for later.
Then you filter your index-value pairs so only even values remain.
Then you pack those pairs into an object in another map.
This can be done more efficiently with a single iteration using reduce:
const vals = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const result = vals.reduce((a, v, i) => {
if (v % 2 == 0) {
a.push({ [i]: v });
}
return a;
}, []);
console.log(result);
Youn can try simple for loop or the reduce function
let arr = [];
for(let i = 0; i<vals.length-1;i += 2)
{
let obj={};
obj[vals[i]]=vals[i+1];
arr.push(obj);
};
I have this array of numbers that i am working with that currently look this;
count = [1,4,3,1,2,3,4,5,6,2,3,5,7];
How can i transform and sort it to make it look like this;
count = [1,2,3,4,5,6,7,1,2,3,3,4,5];
Please help, any idea out there on how to approach this?
1) Get unique elements and sort
2) Get remaining elements and sort
3) combine (1) and (2) arrays.
count = [1, 4, 3, 1, 2, 3, 4, 5, 6, 2, 3, 5, 7];
const spSort = arr => {
const uniq = [...new Set([...arr])];
const rem = [];
const temp_set = new Set([...arr]);
arr.forEach(x => {
if (temp_set.has(x)) {
temp_set.delete(x);
} else {
rem.push(x);
}
});
return [...uniq.sort(), ...rem.sort()];
};
console.log(spSort(count));
Use a Set to create unique numbers and a hash object to keep count of duplicates:
const count = [1, 4, 3, 1, 2, 3, 4, 5, 6, 2, 3, 5, 7];
const hash = count.reduce((obj, num) => {
obj[num] = obj[num] ? ++obj[num] : 1;
return obj;
}, {});
const uniq = [...new Set(count)].sort();
uniq.forEach((num, _, arr) => {
while (--hash[num]) arr.push(num);
});
console.info(uniq);
Trying to solve this challenge on codewars. According to the challenge, the parts of array:
ls = [0, 1, 3, 6, 10]
Are
ls = [0, 1, 3, 6, 10]
ls = [1, 3, 6, 10]
ls = [3, 6, 10]
ls = [6, 10]
ls = [10]
ls = []
And we need to return an array with the sums of those parts.
So my code is as follows:
function partsSums(ls) {
let arrayOfSums = [];
while(ls.length > 0) {
let sum = ls.reduce((a, b) => a + b);
arrayOfSums.push(sum);
ls.shift();
}
return arrayOfSums;
}
console.log(partsSums([0, 1, 3, 6, 10]));
The issue is that it wants us to add the last sum 0 when the array is empty. So we should be getting:
[ 20, 20, 19, 16, 10, 0 ]
Instead of
[ 20, 20, 19, 16, 10]
So I tried this:
function partsSums(ls) {
let arrayOfSums = [];
while(ls.length > 0) {
let sum = ls.reduce((a, b) => a + b);
arrayOfSums.push(sum);
ls.shift();
}
arrayOfSums.push(0);
return arrayOfSums;
}
console.log(partsSums([0, 1, 3, 6, 10]));
And this:
function partsSums(ls) {
ls.push(0);
let arrayOfSums = [];
while(ls.length > 0) {
let sum = ls.reduce((a, b) => a + b);
arrayOfSums.push(sum);
ls.shift();
}
return arrayOfSums;
}
But these caused execution time-out errors on Codewars:
Execution Timed Out (12000 ms)
So I also tried:
function partsSums(ls) {
let arrayOfSums = [];
while(ls.length > -1) {
let sum = ls.reduce((a, b) => a + b);
arrayOfSums.push(sum);
ls.shift();
}
return arrayOfSums;
}
But now this causes a TypeError:
TypeError: Reduce of empty array with no initial value
I am not understanding the concept of how to get 0 into the array when all of the values have been shifted out. The challenge seems to want 0 as the final "sum" of the array, even when the array is empty. But you cannot reduce an empty array - what else can I do here?
EDIT: Tried adding initial value to the reduce method:
function partsSums(ls) {
let arrayOfSums = [];
while(ls.length > 0) {
let sum = ls.reduce((a, b) => a + b, 0);
arrayOfSums.push(sum);
ls.shift();
}
return arrayOfSums;
}
Unfortunately this still fails the basic test :
expected [] to deeply equal [ 0 ]
There is no reason to compute the sum over and over. On a long array this will be very inefficient ( O(n²) ) and might explain your timeout errors. Compute the sum at the beginning and then subtract each element from it in a loop.
ls = [0, 1, 3, 6, 10]
function partsSums(ls) {
let sum = ls.reduce((sum, n) => sum + n, 0)
res = [sum]
for (let i = 1; i <= ls.length; i++){
sum -= ls[i-1]
res.push(sum )
}
return res
}
console.log(partsSums(ls))
Another solution that passed all of the tests:
function partsSums(ls) {
let result = [0],
l = ls.length - 1;
for (let i = l; i >= 0; i--) {
result.push(ls[i] + result[ l - i]);
}
return result.reverse();
}
console.log(partsSums([]));
console.log(partsSums([0, 1, 3, 6, 10]));
console.log(partsSums([1, 2, 3, 4, 5, 6]));
console.log(partsSums([744125, 935, 407, 454, 430, 90, 144, 6710213, 889, 810, 2579358]));
You could use for loop with slice and when i == 0 you can slice len + 1 which is going to return you empty array and sum will be 0.
function partsSums(arr) {
const res = [], len = arr.length
for (let i = len; i > -1; i--) {
res.push(arr.slice(-i || len + 1).reduce((a, n) => a + n, 0))
}
return res;
}
console.log(partsSums([0, 1, 3, 6, 10]));
console.log(partsSums([1, 2, 3, 4, 5, 6]));
console.log(partsSums([744125, 935, 407, 454, 430, 90, 144, 6710213, 889, 810, 2579358]));
You can also use two double reduce and if there is no next element push zero.
function partsSums(arr) {
const sum = arr => arr.reduce((r, e) => r + e, 0);
return arr.reduce((r, e, i, a) => {
const res = sum(a.slice(i, a.length));
return r.concat(!a[i + 1] ? [res, 0] : res)
}, [])
}
console.log(partsSums([0, 1, 3, 6, 10]));
console.log(partsSums([1, 2, 3, 4, 5, 6]));
console.log(partsSums([744125, 935, 407, 454, 430, 90, 144, 6710213, 889, 810, 2579358]));
try this with recursion :
function partsSums(ls) {
let sum = ls.reduce((a, b) => a + b, 0);
return ls.length > 0 ? [sum].concat(partsSums(ls.slice(1))) : [0];
}
console.log(partsSums([0, 1, 3, 6, 10]));
console.log(partsSums([1, 2, 3, 4, 5, 6]));
console.log(partsSums([744125, 935, 407, 454, 430, 90, 144, 6710213, 889, 810, 2579358]));
Here's one thing you could do
function partsSums(ls) {
if(!ls.length) return [0];
let prevTotal = ls.reduce((a,b) => a + b);
return [prevTotal, ...ls.map(val => prevTotal -= val)]
}
console.log(partsSums([0, 1, 3, 6, 10]));
You could iterate from the end and take this value plus the last inserted value of the result set.
This approach works with a single loop and without calculating the maximum sum in advance.
function partsSums(ls) {
var result = [0],
i = ls.length;
while (i--) {
result.unshift(ls[i] + result[0]);
}
return result;
}
console.log(partsSums([0, 1, 3, 6, 10]));
console.log(partsSums([]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
With push and reverse.
function partsSums(ls) {
var result = [0],
l = 0,
i = ls.length;
while (i--) result.push(l += ls[i]);
return result.reverse();
}
console.log(partsSums([0, 1, 3, 6, 10]));
console.log(partsSums([]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an array:
const test = [1,2,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
I want to group the elements of the array into chunks of size 3 (quarters) and size 12 (years):
const quarters = [[1,2,2],[4,5,6],[7,8,9],[10,11,12],[13,14,15],[16,17,18],[19,20]];
const years = [[1,2,2,4,5,6,7,8,9,10,11,12],[13,14,15,16,17,18,19,20]];
I also want to compute the sum of each chunk:
const quarterSums = [5,15,24,33,42,51,39];
const yearSums = [77,132];
How do I do so?
Use a loop that increments by the group size, and use .slice().
EDIT: You added information not in the original question. Since you seem to want the sum of each quarter/year, add this .reduce((s,n)=>s+n, 0) to each subset. This shows a better use of .reduce().
const test = [1, 2, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
console.log(getGroups(test, 3)); // quarters
console.log(getGroups(test, 12)); // years
function getGroups(a, s) {
for (var i = 0, r = []; i < a.length; i += s) {
r.push(a.slice(i, i + s).reduce((s,n)=>s + n, 0));
}
return r;
}
Using something like .reduce() that visits every element makes it more complicated in this case. The traditional for loop provides the benefit of defining how the loop should be incremented.
If you prefer a more function way, I'd still not use .reduce(), but would roll my own tail recursion.
const test = [1, 2, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
console.log(getGroups(test, 3)); // quarters
console.log(getGroups(test, 12)); // years
function getGroups(a, s) {
return function p(a, s, r) {
return !a.length ? r : r.concat(a.slice(0, s).reduce((s,n)=>s + n, 0),
p(a.slice(s), s, r));
}(a, s, []);
}
If you want to group elements into chunks of size n then:
const groupInto = (n, xs) => xs.reduce((xss, x, i) => {
if (i % n === 0) xss.push([]); // create a new group
xss[xss.length - 1].push(x); // push in last group
return xss;
}, []);
const xs = [1,2,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
const quarters = groupInto(3, xs);
const years = groupInto(12, xs);
console.log(JSON.stringify(quarters));
console.log(JSON.stringify(years));
On the other hand, if you want to find the sum of these chunks:
const sumInto = (n, xs) => xs.reduce((ys, x, i) => {
if (i % n === 0) ys.push(0);
ys[ys.length - 1] += x;
return ys;
}, []);
const xs = [1,2,2,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
const quarters = sumInto(3, xs);
const years = sumInto(12, xs);
console.log(JSON.stringify(quarters));
console.log(JSON.stringify(years));
Hope that helps.
You could use a Array#forEach with an object as temporary variable for collecting the values. Then calculate the average.
var values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
temp = { quarter: { avg: [], items: 3, sum: 0 }, year: { avg: [], items: 12, sum: 0 } }
values.forEach(function (v, i) {
Object.keys(temp).forEach(function (k) {
temp[k].sum += v;
if (i && (i + 1) % temp[k].items === 0) {
temp[k].avg.push(temp[k].sum / temp[k].items);
temp[k].sum = 0;
}
});
});
console.log(temp.quarter.avg);
console.log(temp.year.avg);
.as-console-wrapper { max-height: 100% !important; top: 0; }