Related
I like to combine numbers at every 4th index of an array. In the following oversimplified example, I did using "for" loop. Instead of that, I like to learn how to use "map" to achieve the same result. Thanks for any help!
function test() {
var array = [1, 2, 3, 4, 5, 6, 7, 8], arrayNew = [];
for (var n = 0; n < 4; ++n)
arrayNew[n] = array[n] + array[n + 4];
console.log(arrayNew)
}
To use .map, you could iterate the slice of the array that omits the first four elements. During that iteration, the loop index will be 4 units less, so you can grab array[i] and combine it with the currently iterated value from the slice:
const array = [1, 2, 3, 4, 5, 6, 7, 8];
const result = array.slice(4).map((val, i) => array[i] + val);
console.log(result);
If you want to add more than just two values, but want to also add the value at 2n, 3n, ...etc, then you need a nested loop. Here .map is of less use. I would "map" with the use of Array.from, which has a callback function that performs a mapping. Secondly, the sum that has a dynamic number of terms can be performed with reduce:
function accumulate(array, n) {
const groups = Array.from({length: array.length / n});
return Array.from({length: n}, (val, i) =>
groups.reduce((sum, _, j) => sum + array[i + j*n], 0)
);
}
const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
console.log(accumulate(array, 4));
The following was my interview question. But I couldn't crack it and even could not think how to get this done.
var arr = [1,4,5,8,3,2,6,9,7,10];
Expected output of alternate sorting:
[10,1,9,2,8,3,7,4,6,5]
What I have tried:
I tried slicing out the Math.max.apply(null,arr) and Math.min.apply(null,arr) alternatively to push into separate empty array. But It was told that the algorithm is not optimal.
I would sort the array, and then iterate it, picking values from the begining and the end (inloop calculated offsets), in each iteration. A final check to odd arrays would complete the process.
let a = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
a.sort((a, b) => a - b);
let b =[];
let l = a.length-1; // micro optimization
let L = l/2; // micro optimization
for(var i=0; i<L; i++) b.push( a[l-i] ,a[i] );
if(a.length%2) b.push( a[i] ); // add last item in odd arrays
console.log(b);
Result :
b = [10, 1, 9, 2, 8, 3, 7, 4, 6, 5]
Algorithm bennefits:
Avoiding alterations in the original array (through pop and shift), improves the performance considerably.
Precalculating l and L before the loop , prevents the need of being calculated repeatedly in each iteration.
A single conditional cheking at the end of the procces, to handle odd arrays, slightly improves the speed.
I've prepared some PERFORMANCE TESTS, with some of the proposed algorithms :
Original Array(10 items) and Big Array(1000 items)
Here is one way to do it:
var arr = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
// Sort the source array
arr.sort((a, b) => a - b);
// This will be the final result
var result = [];
// Create two pointers
var a = 0,
b = arr.length - 1;
while (result.length < arr.length) {
// Push the elements from start and end to the result array
result.push(arr[b]);
// Avoid bug when array is odd lengthed
if (a !== b) {
result.push(arr[a]);
}
a++;
b--;
}
console.log(result);
The idea is to have two pointers (a and b) traversing the the sorted original array from both the directions and appending the elements in result.
If you assume that the array will be a set of sequential numbers (a good question to ask about the data) you can do this very quickly with no need to sort or mutate the original array(i.e O(n)):
var arr = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
let a = arr.reduce((a, c, i) => {
a[c > arr.length >> 1 ? (arr.length - c) << 1 : (c << 1) - 1] = c
return a
}, [])
console.log(a)
Here's my answer, based off the intuition that you're taking from the front then the back repeatedly from the sorted array until you're empty. The trick is avoiding "max" and "min" which evaluate the entire array, and just sorting it once.
Many of the other answers will put an undefined into the array if the original array has an odd length. I would leave a comment on those but I do not have the reputation. This is why I bounds check twice per loop.
var arr = [1,4,5,8,3,2,6,9,7,10];
// Sort numerically (not lexicographically)
arr.sort((a, b) => a - b)
// The output array
var out = []
// Take from the front, then back until original array is empty
while (true) {
if (arr.length == 0) break
out.push(arr.pop())
if (arr.length == 0) break
out.push(arr.shift())
}
// Output answer
console.log(out)
My solution for readability / no hidden magic:
// Input
var arr = [1,4,5,8,3,2,6,9,7,10];
// Sort
var arr1 = arr.sort((a,b) => (a - b));
// Compose
var arr2 = [];
for (var i = 0; i < arr1.length; i++) {
arr2.push(arr1[(i % 2) === 0
? arr1.length-1-(i/2) // get from end half
: (i-1)/2 // get from begin half
])
}
// Output
console.log(arr2); // = [10, 1, 9, 2, 8, 3, 7, 4, 6, 5]
Their interview answer "that the algorithm is not optimal." is not unexpected ofcourse. I would inquire why they say that, and ask if its really benefitial to spend dollar time on dimes here. (or tens of dollars on cents, actually)
Alternative method with only one variable to increment:
var arr = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
arr = arr.sort((a, b) => b - a);
var result = [];
var a = 0;
while (result.length < arr.length) {
result.push(arr[a]);
result.push(arr[arr.length - a - 1]);
a++;
}
console.log(result);
var a = [1,4,5,8,3,2,6,9,7,10];
var b = a.sort((a, b) => a - b);
var c = a.sort((a, b) => a - b).reverse();
var d = [];
let e = a.length-1;
let f = e/2;
for(let i=0; i<f; i++) d.push( b.pop(), c.pop() );
Replace b and c in the for loop with functions to test:
for(let i=0; i<f; i++) d.push( a.sort((a, b) => a - b).pop(), a.sort((a, b) => a - b).reverse().pop() );
sort the array and divide into two parts , now use reduce to put elements from the two arrays
//original array
var arr = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
//sorting origina array in ascending order
var m = arr.sort(function(a, b) {
return a - b;
});
// diving the sorted array in two parts
let getFirstSet = m.splice(0, arr.length / 2);
// now m containleft over items after the splice
// again sorted it in descending order to avoid back looping
let getSecondSet = m.sort(function(a, b) {
return b - a;
});
//using reduce function
let newArray = getFirstSet.reduce(function(acc, curr, index) {
// pushing element from second array containing 10,9,8,7,6
acc.push(getSecondSet[index]);
// pushing element from first array containing 1,2,3,4,5
acc.push(getFirstSet[index]);
return acc;
}, []); // [] is the initial array where elements will be pushed
console.log(newArray)
Another alternative view ... should this funky sort be done in place, like .sort is?
let input = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
input.sort((a, b) => b - a).every((n, i, a) => (a.splice((i * 2 + 1), 0, a.pop()), (i * 2) < a.length));
console.log(input);
Here is a quick solution, using ternary operators and modulo operator for toggling.
let arr = [1, 4, 5, 8, 3, 2, 6, 9, 7, 10];
let j = 0;
let k = arr.length - 1;
// sort array
arr.sort((a, b) => a - b);
let new_array = [];
for (let i in arr) {
new_array[i] = i % 2 == 0 ? arr[k--] : arr[j++];
}
// prints array
console.log(new_array);
This question already has answers here:
Split array into chunks
(73 answers)
Closed 4 years ago.
I'm trying to implement a chunk function in javascript similar to lodash chunk. It seems like i'm hitting an indexing issue relating to the count here but I can't figure it out.
// chunk array function breaks an array into chunks of defined size
// [1, 2, 3, 4, 5, 6, 7, 8]
// with size 2
// should output: [[1,2], [3,4], [5,6], [7,8]]
const testArr = [1, 2, 3, 4, 5, 6, 7, 8]
const testArr2 = [1, 2, 3, 4, 5, 6, 7]
function chunk(arr, size){
let newArr = []
let tempArr = []
let iterations;
let remainder;
if(Number.isInteger(arr.length / size)){
iterations = arr.length / size
} else {
iterations = size.toString().split('.')[0]
// how many remain?
remainder = arr.length % size
}
// theres an issue somewhere in here relating to count
let count = 0
while(count < iterations){
tempArr = []
for(let i = count; i < (size + count); i++){
tempArr.push(arr[i])
}
newArr.push(tempArr)
count++
}
// if(remainder){
// for(let i = count; i < count + remainder; i++){
// tempArr.push(arr[i])
// }
// }
return newArr
}
console.log(chunk(testArr, 2))
I'm interested in 2 different things:
Whats wrong with my code example?
How would YOU implement this better? Clearly my example is not very
clean and I'm curious how others would handle it (some .map
.reduce stuff maybe?) i tried reading lodash docs but they use a lot of internal functions that make it a little confusing.
actual output is: [ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ] ]
output should be: [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ], [ 7, 8 ] ]
Thanks!
A simpler way to do this would be:
let size = 2;
[1, 2, 3, 4, 5, 6, 7, 8].reduce((carry, current, index) => {
// get the current array bucket. if it doesn't exist, create it.
let el = carry[Math.floor(index / size)] = carry[Math.floor(index / size)] || [];
// push the current element onto the current bucket
el.push(current);
// return our new array
return carry;
}, [])
The issue with your code is just that you need to do:
tempArr.push(arr[i + count])
Is there an elegant, functional way to turn this array:
[ 1, 5, 9, 21 ]
into this
[ [1, 5], [5, 9], [9, 21] ]
I know I could forEach the array and collect the values to create a new array. Is there an elegant way to do that in _.lodash without using a forEach?
You could map a spliced array and check the index. If it is not zero, take the predecessor, otherwise the first element of the original array.
var array = [1, 5, 9, 21],
result = array.slice(1).map((a, i, aa) => [i ? aa[i - 1] : array[0], a]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
An even shorter version, as suggested by Bergi:
var array = [1, 5, 9, 21],
result = array.slice(1).map((a, i) => [array[i], a]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
A fast approach using map would be:
const arr = [ 1, 5, 9, 21 ];
const grouped = arr.map((el, i) => [el, arr[i+1]]).slice(0, -1);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is easily done with array.reduce. What the following does is use an array as aggregator, skips the first item, then for each item after that pushes previous item and the current item as a pair to the array.
const arr = [ 1, 5, 9, 21 ];
const chunked = arr.reduce((p, c, i, a) => i === 0 ? p : (p.push([c, a[i-1]]), p), []);
console.log(chunked);
An expanded version would look like:
const arr = [1, 5, 9, 21];
const chunked = arr.reduce(function(previous, current, index, array) {
if(index === 0){
return previous;
} else {
previous.push([ current, array[index - 1]]);
return previous;
}
}, []);
console.log(chunked);
If you're willing to use another functional library 'ramda', aperture is the function you're looking for.
Example usage taken from the ramda docs:
R.aperture(2, [1, 2, 3, 4, 5]); //=> [[1, 2], [2, 3], [3, 4], [4, 5]]
R.aperture(3, [1, 2, 3, 4, 5]); //=> [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
R.aperture(7, [1, 2, 3, 4, 5]); //=> []
You may do as follows with just a sinle liner of .reduce() with no initial;
var arr = [ 1, 5, 9, 21 ],
pairs = arr.reduce((p,c,i) => i == 1 ? [[p,c]] : p.concat([[p[p.length-1][1],c]]));
console.log(pairs);
I'm sure there is an elegant way, programmatically, but, mathematically I can't help seeing that each new pair has an index difference of 1 from the original array.
If you (later) have the need to turn your array [ 1, 5, 9, 21, 33 ] into [ [1, 9], [5, 21], [9, 33] ], you can use the fact that the difference between the indices is 2.
If you create code for the index difference of 1, extending this would be easy.
Here's slide which has two parameters to control the size of the slice and how many elements are dropped between slices
slide differs from other answers here by giving you these control parameters. other answers here are limited to producing only a slices of 2, or incrementing the slice by 1 each time
// take :: (Int, [a]) -> [a]
const take = (n, xs) =>
xs.slice(0, n)
// drop :: (Int, [a]) -> [a]
const drop = (n, xs) =>
xs.slice(n)
// slice :: (Int, Int, [a]) -> [[a]]
const slide = (m, n, xs) =>
xs.length > m
? [take(m, xs), ...slide(m, n, drop(n, xs))]
: [xs]
const arr = [0,1,2,3,4,5,6]
// log helper improves readability of output in stack snippet
const log = x => console.log(JSON.stringify(x))
log(slide(1, 1, arr))
// [[0],[1],[2],[3],[4],[5],[6]]
log(slide(1, 2, arr))
// [[0],[2],[4],[6]]
log(slide(2, 1, arr))
// [[0,1],[1,2],[2,3],[3,4],[4,5],[5,6]]
log(slide(2, 2, arr))
// [[0,1],[2,3],[4,5],[6]]
log(slide(3, 1, arr))
// [[0,1,2],[1,2,3],[2,3,4],[3,4,5],[4,5,6]]
log(slide(3, 2, arr))
// [[0,1,2],[2,3,4],[4,5,6]]
log(slide(3, 3, arr))
// [[0,1,2],[3,4,5],[6]]
If for some reason you didn't want slide to include partial slices, (slices smaller than m), we could edit it as such
// slice :: (Int, Int, [a]) -> [[a]]
const slide = (m, n, xs) =>
xs.length > m
? [take(m, xs), ...slide(m, n, drop(n, xs))]
: [] // <- return [] instead of [xs]
log(slide(2, 2, arr))
// now prints: [[0,1],[2,3],[4,5]]
// instead of: [[0,1],[2,3],[4,5],[6]]
I noticed that the current solutions, in a way, all look ahead or behind (arr[i + 1] or arr[i - 1]).
It might be useful to also explore an approach that uses reduce and an additional array, defined within a function's closure, to store a to-be-completed partition.
Notes:
Not a one liner, but hopefully easy to understand
part doesn't have to be an array when working with only 2 items, but by using an array, we extend the method to work for n-sized sets of items
If you're not a fan of shift, you can use a combination of slice and redefine part, but I think it's safe here.
partitions with a length less than the required number of elements are not returned
const partition = partitionSize => arr => {
const part = [];
return arr.reduce((parts, x) => {
part.push(x);
if (part.length === partitionSize) {
parts.push(part.slice());
part.shift();
}
return parts;
}, []);
};
const makePairs = partition(2);
const makeTrios = partition(3);
const pairs = makePairs([1,2,3,4,5,6]);
const trios = makeTrios([1,2,3,4,5,6]);
console.log("partition(2)", JSON.stringify(pairs));
console.log("partition(3)", JSON.stringify(trios));
In Javascript, I'm trying to take an initial array of number values and count the elements inside it. Ideally, the result would be two new arrays, the first specifying each unique element, and the second containing the number of times each element occurs. However, I'm open to suggestions on the format of the output.
For example, if the initial array was:
5, 5, 5, 2, 2, 2, 2, 2, 9, 4
Then two new arrays would be created. The first would contain the name of each unique element:
5, 2, 9, 4
The second would contain the number of times that element occurred in the initial array:
3, 5, 1, 1
Because the number 5 occurs three times in the initial array, the number 2 occurs five times and 9 and 4 both appear once.
I've searched a lot for a solution, but nothing seems to work, and everything I've tried myself has wound up being ridiculously complex. Any help would be appreciated!
Thanks :)
You can use an object to hold the results:
const arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
const counts = {};
for (const num of arr) {
counts[num] = counts[num] ? counts[num] + 1 : 1;
}
console.log(counts);
console.log(counts[5], counts[2], counts[9], counts[4]);
So, now your counts object can tell you what the count is for a particular number:
console.log(counts[5]); // logs '3'
If you want to get an array of members, just use the keys() functions
keys(counts); // returns ["5", "2", "9", "4"]
const occurrences = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4].reduce(function (acc, curr) {
return acc[curr] ? ++acc[curr] : acc[curr] = 1, acc
}, {});
console.log(occurrences) // => {2: 5, 4: 1, 5: 3, 9: 1}
const arr = [2, 2, 5, 2, 2, 2, 4, 5, 5, 9];
function foo (array) {
let a = [],
b = [],
arr = [...array], // clone array so we don't change the original when using .sort()
prev;
arr.sort();
for (let element of arr) {
if (element !== prev) {
a.push(element);
b.push(1);
}
else ++b[b.length - 1];
prev = element;
}
return [a, b];
}
const result = foo(arr);
console.log('[' + result[0] + ']','[' + result[1] + ']')
console.log(arr)
One line ES6 solution. So many answers using object as a map but I can't see anyone using an actual Map
const map = arr.reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map());
Use map.keys() to get unique elements
Use map.values() to get the occurrences
Use map.entries() to get the pairs [element, frequency]
var arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]
const map = arr.reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map());
console.info([...map.keys()])
console.info([...map.values()])
console.info([...map.entries()])
If using underscore or lodash, this is the simplest thing to do:
_.countBy(array);
Such that:
_.countBy([5, 5, 5, 2, 2, 2, 2, 2, 9, 4])
=> Object {2: 5, 4: 1, 5: 3, 9: 1}
As pointed out by others, you can then execute the _.keys() and _.values() functions on the result to get just the unique numbers, and their occurrences, respectively. But in my experience, the original object is much easier to deal with.
Don't use two arrays for the result, use an object:
a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
result = { };
for(var i = 0; i < a.length; ++i) {
if(!result[a[i]])
result[a[i]] = 0;
++result[a[i]];
}
Then result will look like:
{
2: 5,
4: 1,
5: 3,
9: 1
}
How about an ECMAScript2015 option.
const a = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
const aCount = new Map([...new Set(a)].map(
x => [x, a.filter(y => y === x).length]
));
aCount.get(5) // 3
aCount.get(2) // 5
aCount.get(9) // 1
aCount.get(4) // 1
This example passes the input array to the Set constructor creating a collection of unique values. The spread syntax then expands these values into a new array so we can call map and translate this into a two-dimensional array of [value, count] pairs - i.e. the following structure:
Array [
[5, 3],
[2, 5],
[9, 1],
[4, 1]
]
The new array is then passed to the Map constructor resulting in an iterable object:
Map {
5 => 3,
2 => 5,
9 => 1,
4 => 1
}
The great thing about a Map object is that it preserves data-types - that is to say aCount.get(5) will return 3 but aCount.get("5") will return undefined. It also allows for any value / type to act as a key meaning this solution will also work with an array of objects.
function frequencies(/* {Array} */ a){
return new Map([...new Set(a)].map(
x => [x, a.filter(y => y === x).length]
));
}
let foo = { value: 'foo' },
bar = { value: 'bar' },
baz = { value: 'baz' };
let aNumbers = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4],
aObjects = [foo, bar, foo, foo, baz, bar];
frequencies(aNumbers).forEach((val, key) => console.log(key + ': ' + val));
frequencies(aObjects).forEach((val, key) => console.log(key.value + ': ' + val));
I think this is the simplest way how to count occurrences with same value in array.
var a = [true, false, false, false];
a.filter(function(value){
return value === false;
}).length
const data = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]
function count(arr) {
return arr.reduce((prev, curr) => (prev[curr] = ++prev[curr] || 1, prev), {})
}
console.log(count(data))
2021's version
The more elegant way is using Logical nullish assignment (x ??= y) combined with Array#reduce() with O(n) time complexity.
The main idea is still using Array#reduce() to aggregate with output as object to get the highest performance (both time and space complexity) in terms of searching & construct bunches of intermediate arrays like other answers.
const arr = [2, 2, 2, 2, 2, 4, 5, 5, 5, 9];
const result = arr.reduce((acc, curr) => {
acc[curr] ??= {[curr]: 0};
acc[curr][curr]++;
return acc;
}, {});
console.log(Object.values(result));
Clean & Refactor code
Using Comma operator (,) syntax.
The comma operator (,) evaluates each of its operands (from left to
right) and returns the value of the last operand.
const arr = [2, 2, 2, 2, 2, 4, 5, 5, 5, 9];
const result = arr.reduce((acc, curr) => (acc[curr] = (acc[curr] || 0) + 1, acc), {});
console.log(result);
Output
{
"2": 5,
"4": 1,
"5": 3,
"9": 1
}
If you favour a single liner.
arr.reduce(function(countMap, word) {countMap[word] = ++countMap[word] || 1;return countMap}, {});
Edit (6/12/2015):
The Explanation from the inside out.
countMap is a map that maps a word with its frequency, which we can see the anonymous function. What reduce does is apply the function with arguments as all the array elements and countMap being passed as the return value of the last function call. The last parameter ({}) is the default value of countMap for the first function call.
ES6 version should be much simplifier (another one line solution)
let arr = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
let acc = arr.reduce((acc, val) => acc.set(val, 1 + (acc.get(val) || 0)), new Map());
console.log(acc);
// output: Map { 5 => 3, 2 => 5, 9 => 1, 4 => 1 }
A Map instead of plain Object helping us to distinguish different type of elements, or else all counting are base on strings
Edit 2020: this is a pretty old answer (nine years). Extending the native prototype will always generate discussion. Although I think the programmer is free to choose her own programming style, here's a (more modern) approach to the problem without extending Array.prototype:
{
// create array with some pseudo random values (1 - 5)
const arr = Array.from({length: 100})
.map( () => Math.floor(1 + Math.random() * 5) );
// frequencies using a reducer
const arrFrequencies = arr.reduce((acc, value) =>
({ ...acc, [value]: acc[value] + 1 || 1}), {} )
console.log(arrFrequencies);
console.log(`Value 4 occurs ${arrFrequencies[4]} times in arrFrequencies`);
// bonus: restore Array from frequencies
const arrRestored = Object.entries(arrFrequencies)
.reduce( (acc, [key, value]) => acc.concat(Array(value).fill(+key)), [] );
console.log(arrRestored.join());
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
The old (2011) answer: you could extend Array.prototype, like this:
{
Array.prototype.frequencies = function() {
var l = this.length,
result = {
all: []
};
while (l--) {
result[this[l]] = result[this[l]] ? ++result[this[l]] : 1;
}
// all pairs (label, frequencies) to an array of arrays(2)
for (var l in result) {
if (result.hasOwnProperty(l) && l !== 'all') {
result.all.push([l, result[l]]);
}
}
return result;
};
var freqs = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4].frequencies();
console.log(`freqs[2]: ${freqs[2]}`); //=> 5
// or
var freqs = '1,1,2,one,one,2,2,22,three,four,five,three,three,five'
.split(',')
.frequencies();
console.log(`freqs.three: ${freqs.three}`); //=> 3
// Alternatively you can utilize Array.map:
Array.prototype.frequencies = function() {
var freqs = {
sum: 0
};
this.map(function(a) {
if (!(a in this)) {
this[a] = 1;
} else {
this[a] += 1;
}
this.sum += 1;
return a;
}, freqs);
return freqs;
}
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
Based on answer of #adamse and #pmandell (which I upvote), in ES6 you can do it in one line:
2017 edit: I use || to reduce code size and make it more readable.
var a=[7,1,7,2,2,7,3,3,3,7,,7,7,7];
alert(JSON.stringify(
a.reduce((r,k)=>{r[k]=1+r[k]||1;return r},{})
));
It can be used to count characters:
var s="ABRACADABRA";
alert(JSON.stringify(
s.split('').reduce((a, c)=>{a[c]++?0:a[c]=1;return a},{})
));
A shorter version using reduce and tilde (~) operator.
const data = [2, 2, 2, 2, 2, 4, 5, 5, 5, 9];
function freq(nums) {
return nums.reduce((acc, curr) => {
acc[curr] = -~acc[curr];
return acc;
}, {});
}
console.log(freq(data));
If you are using underscore you can go the functional route
a = ['foo', 'foo', 'bar'];
var results = _.reduce(a,function(counts,key){ counts[key]++; return counts },
_.object( _.map( _.uniq(a), function(key) { return [key, 0] })))
so your first array is
_.keys(results)
and the second array is
_.values(results)
most of this will default to native javascript functions if they are available
demo : http://jsfiddle.net/dAaUU/
So here's how I'd do it with some of the newest javascript features:
First, reduce the array to a Map of the counts:
let countMap = array.reduce(
(map, value) => {map.set(value, (map.get(value) || 0) + 1); return map},
new Map()
)
By using a Map, your starting array can contain any type of object, and the counts will be correct. Without a Map, some types of objects will give you strange counts.
See the Map docs for more info on the differences.
This could also be done with an object if all your values are symbols, numbers, or strings:
let countObject = array.reduce(
(map, value) => { map[value] = (map[value] || 0) + 1; return map },
{}
)
Or slightly fancier in a functional way without mutation, using destructuring and object spread syntax:
let countObject = array.reduce(
(value, {[value]: count = 0, ...rest}) => ({ [value]: count + 1, ...rest }),
{}
)
At this point, you can use the Map or object for your counts (and the map is directly iterable, unlike an object), or convert it to two arrays.
For the Map:
countMap.forEach((count, value) => console.log(`value: ${value}, count: ${count}`)
let values = countMap.keys()
let counts = countMap.values()
Or for the object:
Object
.entries(countObject) // convert to array of [key, valueAtKey] pairs
.forEach(([value, count]) => console.log(`value: ${value}, count: ${count}`)
let values = Object.keys(countObject)
let counts = Object.values(countObject)
var array = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
function countDuplicates(obj, num){
obj[num] = (++obj[num] || 1);
return obj;
}
var answer = array.reduce(countDuplicates, {});
// answer => {2:5, 4:1, 5:3, 9:1};
If you still want two arrays, then you could use answer like this...
var uniqueNums = Object.keys(answer);
// uniqueNums => ["2", "4", "5", "9"];
var countOfNums = Object.keys(answer).map(key => answer[key]);
// countOfNums => [5, 1, 3, 1];
Or if you want uniqueNums to be numbers
var uniqueNums = Object.keys(answer).map(key => +key);
// uniqueNums => [2, 4, 5, 9];
Here's just something light and easy for the eyes...
function count(a,i){
var result = 0;
for(var o in a)
if(a[o] == i)
result++;
return result;
}
Edit: And since you want all the occurences...
function count(a){
var result = {};
for(var i in a){
if(result[a[i]] == undefined) result[a[i]] = 0;
result[a[i]]++;
}
return result;
}
Solution using a map with O(n) time complexity.
var arr = [2, 2, 2, 2, 2, 4, 5, 5, 5, 9];
const countOccurrences = (arr) => {
const map = {};
for ( var i = 0; i < arr.length; i++ ) {
map[arr[i]] = ~~map[arr[i]] + 1;
}
return map;
}
Demo: http://jsfiddle.net/simevidas/bnACW/
There is a much better and easy way that we can do this using ramda.js.
Code sample here
const ary = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
R.countBy(r=> r)(ary)
countBy documentation is at documentation
I know this question is old but I realized there are too few solutions where you get the count array as asked with a minimal code so here is mine
// The initial array we want to count occurences
var initial = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
// The count array asked for
var count = Array.from(new Set(initial)).map(val => initial.filter(v => v === val).length);
// Outputs [ 3, 5, 1, 1 ]
Beside you can get the set from that initial array with
var set = Array.from(new Set(initial));
//set = [5, 2, 9, 4]
My solution with ramda:
const testArray = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]
const counfFrequency = R.compose(
R.map(R.length),
R.groupBy(R.identity),
)
counfFrequency(testArray)
Link to REPL.
Using MAP you can have 2 arrays in the output: One containing the occurrences & the other one is containing the number of occurrences.
const dataset = [2,2,4,2,6,4,7,8,5,6,7,10,10,10,15];
let values = [];
let keys = [];
var mapWithOccurences = dataset.reduce((a,c) => {
if(a.has(c)) a.set(c,a.get(c)+1);
else a.set(c,1);
return a;
}, new Map())
.forEach((value, key, map) => {
keys.push(key);
values.push(value);
});
console.log(keys)
console.log(values)
This question is more than 8 years old and many, many answers do not really take ES6 and its numerous advantages into account.
Perhaps is even more important to think about the consequences of our code for garbage collection/memory management whenever we create additional arrays, make double or triple copies of arrays or even convert arrays into objects. These are trivial observations for small applications but if scale is a long term objective then think about these, thoroughly.
If you just need a "counter" for specific data types and the starting point is an array (I assume you want therefore an ordered list and take advantage of the many properties and methods arrays offer), you can just simply iterate through array1 and populate array2 with the values and number of occurrences for these values found in array1.
As simple as that.
Example of simple class SimpleCounter (ES6) for Object Oriented Programming and Object Oriented Design
class SimpleCounter {
constructor(rawList){ // input array type
this.rawList = rawList;
this.finalList = [];
}
mapValues(){ // returns a new array
this.rawList.forEach(value => {
this.finalList[value] ? this.finalList[value]++ : this.finalList[value] = 1;
});
this.rawList = null; // remove array1 for garbage collection
return this.finalList;
}
}
module.exports = SimpleCounter;
Using Lodash
const values = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
const frequency = _.map(_.groupBy(values), val => ({ value: val[0], frequency: val.length }));
console.log(frequency);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
To return an array which is then sortable:
let array = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4]
let reducedArray = array.reduce( (acc, curr, _, arr) => {
if (acc.length == 0) acc.push({item: curr, count: 1})
else if (acc.findIndex(f => f.item === curr ) === -1) acc.push({item: curr, count: 1})
else ++acc[acc.findIndex(f => f.item === curr)].count
return acc
}, []);
console.log(reducedArray.sort((a,b) => b.count - a.count ))
/*
Output:
[
{
"item": 2,
"count": 5
},
{
"item": 5,
"count": 3
},
{
"item": 9,
"count": 1
},
{
"item": 4,
"count": 1
}
]
*/
Check out the code below.
<html>
<head>
<script>
// array with values
var ar = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
var Unique = []; // we'll store a list of unique values in here
var Counts = []; // we'll store the number of occurances in here
for(var i in ar)
{
var Index = ar[i];
Unique[Index] = ar[i];
if(typeof(Counts[Index])=='undefined')
Counts[Index]=1;
else
Counts[Index]++;
}
// remove empty items
Unique = Unique.filter(function(){ return true});
Counts = Counts.filter(function(){ return true});
alert(ar.join(','));
alert(Unique.join(','));
alert(Counts.join(','));
var a=[];
for(var i=0; i<Unique.length; i++)
{
a.push(Unique[i] + ':' + Counts[i] + 'x');
}
alert(a.join(', '));
</script>
</head>
<body>
</body>
</html>
function countOcurrences(arr){
return arr.reduce((aggregator, value, index, array) => {
if(!aggregator[value]){
return aggregator = {...aggregator, [value]: 1};
}else{
return aggregator = {...aggregator, [value]:++aggregator[value]};
}
}, {})
}
You can simplify this a bit by extending your arrays with a count function. It works similar to Ruby’s Array#count, if you’re familiar with it.
Array.prototype.count = function(obj){
var count = this.length;
if(typeof(obj) !== "undefined"){
var array = this.slice(0), count = 0; // clone array and reset count
for(i = 0; i < array.length; i++){
if(array[i] == obj){ count++ }
}
}
return count;
}
Usage:
let array = ['a', 'b', 'd', 'a', 'c'];
array.count('a'); // => 2
array.count('b'); // => 1
array.count('e'); // => 0
array.count(); // => 5
Gist
Edit
You can then get your first array, with each occurred item, using Array#filter:
let occurred = [];
array.filter(function(item) {
if (!occurred.includes(item)) {
occurred.push(item);
return true;
}
}); // => ["a", "b", "d", "c"]
And your second array, with the number of occurrences, using Array#count into Array#map:
occurred.map(array.count.bind(array)); // => [2, 1, 1, 1]
Alternatively, if order is irrelevant, you can just return it as a key-value pair:
let occurrences = {}
occurred.forEach(function(item) { occurrences[item] = array.count(item) });
occurences; // => {2: 5, 4: 1, 5: 3, 9: 1}