Count the repetition of an element in an array using a function with one parameter - javascript

Good Day, I am trying to count how many times a particular element in an array appears. I tried but my code below counts only one of the array even if it appears more than once (this is not the problem). I want it to return the amount of time each element appears. For example
let arr = [1, 3, 2, 1];
this should return
{1:2} {3:1} {2:1}
My code returns 3 (as in it just doesn't count one twice)
How do i go about this?
Below is my code
function numberCount(number) {
let count = 0;
number.forEach(function (item, index) {
if (number.indexOf(item) == index) count++;
});
console.log(count);
}

While iterating over number (better to call it arr, it's an array, not a number), use an object to keep track of the number of times each number has occured so far. Then, iterate over the resulting object's entries to create the objects desired:
let arr = [1, 3, 2, 1];
function numberCount(arr) {
let count = 0;
const obj = arr.reduce((a, num) => {
a[num] = (a[num] || 0) + 1;
return a;
}, {});
return Object.entries(obj).map(([key, val]) => ({ [key]: val }));
}
console.log(numberCount(arr));
Numeric keys always come in numeric order in an object. If you want the objects in the output to come in insertion order (eg, the object with key 3 before the object with key 2), then use a Map instead of an object (map keys will be iterated over in insertion order):
let arr = [1, 3, 2, 1];
function numberCount(arr) {
let count = 0;
const map = arr.reduce((a, num) => (
a.set(num, (a.get(num) || 0) + 1)
), new Map());
return [...map.entries()]
.map(([key, val]) => ({ [key]: val }));
}
console.log(numberCount(arr));

You should filter out these numbers, then use the length:
let arr = [1, 3, 2, 1];
function itemCount(array) {
var sorted = array.sort()
var uniqueCount = sorted.filter((v, i, a) => a.indexOf(v) == i);
var count = [];
uniqueCount.forEach(item => {
var itemCount = sorted.filter(e => e == item).length;
count.push({[item]: itemCount});
});
return count;
}
console.log(itemCount(arr));

I would suggest not reinventing the wheel, and instead use lodash which already has this function. Using countBy() you will get an object you can then convert into your desired result. For example:
const arr = [1, 3, 2, 1]
const count = _.countBy(arr)
const result = Object.keys(count).map(k => ({ [k]: count[k] }))
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>

Related

Count all values in object where X is the first key letter

I would like to count all values where a letter appears first and return the letter with atleast half of all values in my object so for example I assuming I have an object like this
const sample = { "A,B,C": 4, "B,C,A": 3, "C,B,A": 2, "A,C,B": 2 };
I would return A because if you count all the values where A appears first you would get 6 (4+2)
This is what I currently have:
for (let votes of Object.values(sample)) {
sum += votes
}
stretchWin = Math.round(sum / 2)
winner = Object.entries(sample)
.filter(([, val]) => val >= stretchWin)
.map(([keys]) => keys)
With this I am getting an empty array because I am not counting all the values assigned to A
Iterate over the whole sample first to get a sum of the values by the first letter first, then iterate over that new object to identify which values match the target of half the total.
const sample = {
"A,B,C": 4,
"B,C,A": 3,
"C,B,A": 2,
"A,C,B": 2
};
const sumByChar = {};
for (const [key, value] of Object.entries(sample)) {
const char = key[0];
sumByChar[char] = (sumByChar[char] ?? 0) + value;
}
let sum = 0;
for (let votes of Object.values(sample)) {
sum += votes
}
const targetSum = Math.round(sum / 2);
const winners = Object.entries(sumByChar)
.filter(([, val]) => val >= targetSum)
.map(([key]) => key);
console.log(winners);
I'm not completely sure what you mean what the outcome should be. If I understand correctly you want something like this??
const sample = { "A,B,C": 4, "B,C,A": 3, "C,B,A": 2, "A,C,B": 2 };
const totalSum = Object.values(sample).reduce(
(previousValue, currentValue) => previousValue + currentValue,
0
);
const stretchWin = Math.round(totalSum / 2);
const winner = Object.entries(sample)
.filter(([key, value]) => {
const isFirstLetterA = key.startsWith("A");
return isFirstLetterA || value >= stretchWin;
})
.map(([key, value]) => value)
.reduce((previousValue, currentValue) => previousValue + currentValue, 0);
console.log(winner);

How to get the excluded elements when using javascript's filter

Javascript's filter returns the array with all the elements passing the test.
How how can you easily get all the elements that failed the test without running the test again, but for the converse? How is the best way to do it, even if you have to run the test again.
let arr; // this is the array on which the filter will be run [SET ELSEWHERE]
let fn; // The filter function [SET ELSEWHERE]
let goodElements; // This will be the new array of the good elements passing the test
let badElements; // This will be the new array of the elements failing the test
goodElements = arr.filter(fn);
// SO HOW IS badElements set????
How is badElements set?
If you don't want to do two iterations, you can use a for loop and a ternary operator:
let arr = [1, 2, 3];
let fn = (e) => e % 2 == 0;
let goodElements = [];
let badElements = [];
for(const e of arr) (fn(e) ? goodElements : badElements).push(e);
console.log(goodElements);
console.log(badElements);
Otherwise, just invert the condition with the ! operator:
let arr = [1, 2, 3];
let fn = (e) => e % 2 == 0;
let goodElements;
let badElements;
goodElements = arr.filter(fn);
badElements = arr.filter(e => !fn(e));
console.log(goodElements);
console.log(badElements);
Don't use filter(). If you want to partition the data into two arrays, do it yourself.
function partition(array, fn) {
let goodArray = [],
badArray = [];
array.forEach(el => {
if (fn(el)) {
goodArray.push(el);
} else {
badArray.push(el);
}
});
return [goodArray, badArray];
}
let [goodElemements, badElements] = partition(arr, fn);
You could also use reduce()
function partition(array, fn) {
return array.reduce(acc, el => {
if (fn(el)) {
acc[0].push(el);
} else {
acc[1].push(el);
}
}, [[],[]]);
}
let [goodElemements, badElements] = partition(arr, fn);
If you want to do this strictly with Array.filter and only one loop, then consider something like this:
UPD: based on #AlvaroFlaƱoLarrondo comment, added external condition function to current approach.
// Array of elements
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
// External filter method
const fn = e => e.length > 6;
// Define empty bad array
const bad = [];
// Define good array as result from filter function
const good = words.filter(word => {
// In filter condition return good values
if(fn(word)) return word;
// And skip else values by just pushing them
// to bad array, without returning
else bad.push(word);
});
// Results
console.log(good);
console.log(bad);
You could use reduce in order to split the array into two arrays based on a predicate, like in this example:
const arr = [1, 0, true, false, "", "foo"];
const fn = element => !element;
const [goodElements, badElements] = arr.reduce(
([truthies, falsies], cur) =>
fn(cur) ? [truthies, [...falsies, cur]] : [[...truthies, cur], falsies],
[[], []]
);
console.log(goodElements, badElements);
I see two possible routes:
// in this case, if it's not in `goodElements`, it's a bad 'un
badElements = arr.filter( el => !goodElements.includes(el) );
or this:
// we don't **know** if fn needs the optional parameters, so we will
// simply pass them. If it doesn't need 'em, they'll be ignored.
badElements = arr.filter( (el, idx, arr) => !fn(el, idx, arr) );
const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
const fn = (x) => x % 2 === 0
const removeItems = (array, itemsToRemove) => {
return array.filter(v => {
return !itemsToRemove.includes(v);
});
}
const goodElements = arr.filter(fn)
console.log(goodElements) // [ 0, 2, 4, 6, 8 ]
const badElements = removeItems(arr, goodElements)
console.log(badElements) // [ 1, 3, 5, 7, 9 ]

Javascript: Remove element from array

I have an array and I want to delete some elements. My array is arrayAuxiliar. My code is the following one:
for( var i = 0; i < arrayAuxiliar.length; i++)
{
if (arrayAuxiliar[i] == valueFirts) {
arrayAuxiliar.splice(i,1);
}
else if(arrayAuxiliar[i] == value){
arrayAuxiliar.splice(i,1);
}
}
Example of what is happening:
Initial values: arrayAuxiliar = [3,2,1], valueFirst = 1, value = 2
Final values: arrayAuxiliar = [3,1]
I know this happens because splice() changes the original array and that's for this reason the comparasion between arrayAuxiliar[i] == valueFirts is never true. I've also tried to use remove arrayAuxiliar[i] but it returns [3,,].
How can I solve this situation in order to get the only element that does not verify the conditions which is 3? The idea was the final result be [3] and I could get it by a arrayAuxiliar[0] command.
Use filter and includes method
const array = [3, 2, 1]
const remove_list = [2, 1]
const res = array.filter(num => !remove_list.includes(num))
console.log(res)
I think using filter would be more appropiate. You can try:
let arrayAuxiliar = [3, 2, 1]
const valueFirst = 1
const value = 2
arrayAuxiliar = arrayAuxiliar.filter(i => i !== valueFirst && i !== value)
console.log(arrayAuxiliar)
I would use Array.prototype.filter():
const arr = [3,2,1];
const valueFirst = 1;
const value = 2;
console.log(arr.filter(item => ![value, valueFirst].includes(item)));
Also you can use reduce function
function test(arrayAuxiliar, valueFirts, value) {
return arrayAuxiliar.reduce((acc,rec) => {
if ((rec === value) || (rec === valueFirts)) {
return [...acc]
}
return [...acc, rec]
}, [])
}
console.log(test([3,2,1,4,1,2,2,3,1,4,5,8,2,5], 1, 2))

Javascript - Counting array elements by reduce method until specific value occurs doesn't give a correct output

const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total)=>(num==0 ? total : total+num), 0)
console.log(sum(arr, 0))
Please check how can I make it work. Did some mistake but don't know what exactly. Output is a function instead of a result.
This is awkward to do in .reduce because it goes through the entire array. If we do a naive implementation you can see the problem:
const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total, x)=>(num==x ? total : total+x), 0)
console.log(sum(arr, 0))
We now make the check correctly - num==x will return true when x is zero (the value of num). However, the result is wrong because this only returns true once but any other iteration it's still true. And here is the same thing with more logging that describes each step of the process:
const arr = [5,6,0,7,8];
const sum = (arr,num) => arr.reduce((total, x)=> {
const boolCheck = num==x;
const result = boolCheck ? total : total+x;
console.log(
`total: ${total}
num: ${num}
x: ${x}
boolCheck: ${boolCheck}
result: ${result}`);
return result;
}, 0)
console.log(sum(arr, 0))
So, you need to add some flag that persists between iterations, so it doesn't get lost.
One option is to have an external flag that you change within the reduce callback:
const arr = [5,6,0,7,8];
const sum = (arr,num) => {
let finished = false;
return arr.reduce((total, x) => {
if(x === num)
finished = true;
return finished ? total : total+x;
}, 0)
}
console.log(sum(arr, 0))
Alternatively, you can have that flag internal to the reduce callback and pass it around between calls. It works the same way in the end but makes the callback function pure. At the cost of some unorthodox construct:
const arr = [5,6,0,7,8];
const sum = (arr,num) => {
return arr.reduce(({total, finished}, x) => {
if(x === num)
finished = true;
total = finished ? total : total+x;
return {total, finished};
}, {total: 0, finished: false})
.total
}
console.log(sum(arr, 0))
If you want to use reduce but you're OK with using other methods, then you can use Array#indexOf to find the first instance of a value and Array#slice the array that contains any value up to the target value:
const arr = [5,6,0,7,8];
const sum = (arr,num) => {
const endIndex = arr.indexOf(num);
return arr.slice(0, endIndex)
.reduce((total, x)=> total+x, 0)
}
console.log(sum(arr, 0))
Or in as one chained expression:
const arr = [5,6,0,7,8];
const sum = (arr,num) => arr
.slice(0, arr.indexOf(num))
.reduce((total, x)=> total+x, 0);
console.log(sum(arr, 0))
Other libraries may have a takeUntil or takeWhile operation which is even closer to what you want - it gets you an array from the beginning up to a given value or condition. You can then reduce the result of that.
Here is an example of this using Lodash#takeWhile
By using chaining here, Lodash will do lazy evaluation, so it will only go through the array once, instead of scanning once to find the end index and going through the array again to sum it.
const arr = [5,6,0,7,8];
const sum = (arr,num) => _(arr)
.takeWhile(x => x !== num)
.reduce((total, x)=>total+x, 0)
console.log(sum(arr, 0))
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
As a note, if you are using Lodash, then you may as well use _.sum(). I didn't above just to illustrate how a generic takeUntil/takeWhile looks.
const arr = [5, 6, 0, 7, 8];
const sum = (arr, num) => _(arr)
.takeWhile(x => x !== num)
.sum()
console.log(sum(arr, 0))
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
Since you need to stop summing values part way through the array, this might be most simply implemented using a for loop:
const arr = [5, 6, 0, 7, 8];
const num = 0;
let sum = 0;
for (let i = 0; i < arr.length; i++) {
if (arr[i] == num) break;
sum += arr[i];
}
console.log(sum);
If you want to use reduce, you need to keep a flag that says whether you have seen the num value so you can stop adding values from the array:
const arr = [5, 6, 0, 7, 8];
const sum = (arr, num) => {
let seen = false;
return arr.reduce((c, v) => {
if (seen || v == num) {
seen = true;
return c;
}
return c + v;
}, 0);
}
console.log(sum(arr, 0));
console.log(sum(arr, 8));
call it as follows:
console.log(sum(arr, 0)());
You need parenthesis to execute the function ()
sum(arr, 0)
Without parenthesis you store a reference to the function in the variable

How to get "unfiltered" array items?

Let's say I have an array which I filter by calling myItems.filter(filterFunction1) and get some items from it.
Then I want to run another filtering function filterFunction2 against the remaining items which were not selected by filterFunction1.
Is that possible to get the remaining items that were left out after calling a filtering function?
You'd have to rerun the filter with an inverted predicate, which seems wasteful. You should reduce the items instead and bin them into one of two bins:
const result = arr.reduce((res, item) => {
res[predicate(item) ? 'a' : 'b'].push(item);
return res;
}, { a: [], b: [] });
predicate here is the callback you'd give to filter.
Unfortunately, there is no one-step solution based on filter. Still the solution is a simple one-liner:
Here's an example
const arr = [ 1,2,3,4,5,6,7,8 ];
const filtered = arr.filter(x=>!!(x%2))
const remaining = arr.filter(x=>!filtered.includes(x))
console.log(filtered, remaining);
You could map an array of flags and then filter by the flags values.
const cond = v => !(v % 2);
var array = [1, 2, 3, 4, 5],
flags = array.map(cond),
result1 = array.filter((_, i) => flags[i]),
result2 = array.filter((_, i) => !flags[i]);
console.log(result1);
console.log(result2);
You can achieve that using Array.reduce.
const myItems = [...];
const { result1, result2 } = myItems.reduce(
(result, item) => {
if (filterFunc1(item)) {
result.result1.push(item);
} else if (filterFunc2(item)) {
result.result2.push(item);
}
return result;
},
{ result1: [], result2: [] },
);
If you don't want to use reduce, you may want to iterate the array once and acquire the filtered and unfiltered items in a single shot, using a plain efficient for..of loop:
function filterAndDiscriminate(arr, filterCallback) {
const res = [[],[]];
for (var item of arr) {
res[~~filterCallback(item)].push(item);
}
return res;
}
const [unfiltered, filtered] = filterAndDiscriminate([1,2,3,4,5], i => i <= 3);
console.log(filtered, unfiltered);
There's a way more simple and readable way to do this:
const array1 = []
const array2 = []
itemsToFilter.forEach(item => item.condition === met ? array1.push(challenge) : array2.push(challenge))

Categories