Phoenix Shield
Phoenix Shield

Reputation: 171

How to count the number of times specific characters are in a string

I am trying to create a function that looks to see if any of the characters in an array are in a string, and if so, how many.

I have tried to calculate every pattern, but there is too much. I tried using the alternative to the "in" operator from Python, but that didn't work out as well

function calc_fit(element) {
  var fitness_let = ["e", "l", "m", "n", "t"]

  }
}

The element is the string, and the fitness_let array is the array of things that I need to check to see if they are in the string, and if so, how many.

Upvotes: 16

Views: 1102

Answers (6)

sumit
sumit

Reputation: 15464

You can count occurrences with an identical value of an array using map and filter:

let str="I love JavaScript and Node.js ";
let arr=str.replace(/[^a-zA-Z]/g, '').split('');

const mapped = [...new Set(arr)].map(a => `${a} occurs ${arr.filter(a1 => a1 === a).length  } time(s)`);
console.log(mapped);

Upvotes: 11

briosheje
briosheje

Reputation: 7436

Here is a slightly different approach relying on function generators.

There is no relevant reason for using this over other solutions, but it allows to give additional control on the whole cycle.

As a side note, the string is iterated only once, so the whole "iteration" cycle should be quite fast.

Explanation is directly in the code below.

The output is an object where each key is a char and holds the amount of occurrences, holding all the searched needles.

If the array of searched characters is not passed, it is automatically built inside the calc_fit function, this will return both uppercase and lowercase occurrences separately, including punctuation and symbols. This is easily customizable though.

// Iterates a string and yield the current looped char if it exists in the chars list.
function* matchChars(s, chars) {
  for (var char of s) {
    if (chars.indexOf(char) > -1) yield { char: char };
  }
}

// not sure why the function was named in this way in the original code, but hey, it's the OP's function name.
function calc_fit(element, chars) {
  chars = chars || [...new Set(element)];
  // builds an object from the above array, where the structure has the key which is the char, and the value which is initially 0.
  const matchList = chars.reduce((acc,next) => (acc[next] = 0, acc), {});
  // Iterates all the matches. For each match, it increments the amount of matches of matchList.
  for (var match of matchChars(element, chars)) matchList[match.char]++;
  // finally, returns matchList.
  return matchList;
}

// assertions: should match all the characters.
console.log(calc_fit('element', ["e", "l", "m", "n", "t"]));
// assertions: should return all zeros.
console.log(calc_fit('', ["e", "l", "m", "n", "t"]));
// assertions: should automatically detect chars, even upper case and lower case.
console.log(calc_fit('hello, world. ThIs is beatiful!'));

Upvotes: 0

Shidersz
Shidersz

Reputation: 17190

First, and to generalize the method, it will be better if calc_fit() takes the array of letters as an argument too. Then, you can create a Map from the array with the counter of each letter starting on 0. Finally, you traverse the string and increment the respective counter of each letter when needed.

function calc_fit(element, fitness_let)
{
    // Create a Map from the array of letters to search.
    let map = new Map(fitness_let.map(l => ([l, 0])));

    // Traverse the string and increment counter of letters.    
    for (const c of element)
    {
        if (map.has(c))
            map.set(c, map.get(c) + 1);
    }
    
    return map;
}

let res = calc_fit("This is a string with some letters", ["e","l","m","n","t"]);
res.forEach((counter, letter) => console.log(`${letter} => ${counter}`));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

Upvotes: 3

mwilson
mwilson

Reputation: 12900

You could create a hash map and leverage reduce to count all instances found

Example:

const counts = {};
["e", "l", "m", "n", "t"].forEach( e => counts[e] = 0 );
const letters = "hey look! a string!".split("");
const results = letters.reduce( (acc, curr) => {
	if (acc.hasOwnProperty(curr)) { acc[curr] += 1; }
  return acc;
}, counts);


console.log(results);

Upvotes: 0

Code Maniac
Code Maniac

Reputation: 37755

If you want to get no. of times each character appeared than you can use reduce and Map

let getTotal = (element) => {
  let fitness = ["e", "l", "m", "n", "t"]
  let newMap = new Map(fitness.map(v=>[v,v]))
  return element.split('').reduce((op,inp)=>{
    if(newMap.has(inp)){
      op[inp] = op[inp] || 0
      op[inp]++
    }
    return op
  },{})
}

console.log(getTotal('element'))
console.log(getTotal('eleabc'))


You can use join to build a regex with alternation | and word boundaries, to get total number

let getTotal = (element) =>{
  let fitness = ["e", "l", "m", "n", "t"]
  let reg = '\\b' + fitness.join('|') + '\\b'
  let pattern = new RegExp(reg,'gi')
  return (element.match(pattern) || []).length
}

console.log(getTotal('element'))
console.log(getTotal('eleabc'))

Upvotes: 0

Tim Biegeleisen
Tim Biegeleisen

Reputation: 520918

One approach would be to iterate the array and do global regex removals of each letter. Then, compare the replaced string length against the original input length to determine the number of occurrences.

function calc_fit(element) {
var fitness_let = ["e", "l", "m", "n", "t"];

for (var i=0; i < fitness_let.length; i++) {
    var letter = fitness_let[i];
    var numtimes = element.length - element.replace(new RegExp(letter, 'g'), '').length;
    console.log(fitness_let[i] + " occurs: " + numtimes + " times.");
}

}

var input = "elements are elemental";
calc_fit(input);

Upvotes: 0

Related Questions