24

I need to write some kind of loop that can count the frequency of each letter in a string.

For example: "aabsssd"

output: a:2, b:1, s:3, d:1

Also want to map same character as property name in object. Any good idea how to do this?

I am not sure how to do it.

This is where I am so far:

var arr = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];

function counter(x) {
  var count = 0,
    temp = [];
  x = x.split('');
  console.log(x);
  for (var i = 0, len = x.length; i < len; i++) {
    if (x[i] == "a") {
      count++;
    }
  }
  return count;
}
var a = "aabbddd";
console.log(counter(a));
Penny Liu
  • 15,447
  • 5
  • 79
  • 98
Samrat
  • 387
  • 1
  • 4
  • 13

21 Answers21

39

Here you go:

function getFrequency(string) {
    var freq = {};
    for (var i=0; i<string.length;i++) {
        var character = string.charAt(i);
        if (freq[character]) {
           freq[character]++;
        } else {
           freq[character] = 1;
        }
    }

    return freq;
};
Jonathan Crowe
  • 5,793
  • 1
  • 18
  • 28
  • Can any one please explain how the **if (freq[character])** this works in above code. It perfectly solve my problem. I used debugger to find how it works. But still not getting whats the logic behind it. Thanks – M Sohaib Khan Apr 07 '16 at 21:34
  • 3
    In javascript an `undefined` value equates to false. By checking `if(freq[character])` you are checking if the value of `freq[character]` is falsey (ie: `undefined`, 0, `null`, etc.). Since we initialize the value of a matched character at `1` that if statement will only ever be entered if the character has been encountered and counted previously, otherwise we initialize it – Jonathan Crowe Apr 11 '16 at 21:27
  • Above code can be replaced by one liner (freq[character] = freq[character] + 1 || 1) it works but when i replace with freq[character] = freq[character]++ || 1 it should work but it does not it always count 1 for all character . Why? Could you explain? – Infinity Aug 05 '21 at 17:29
25

some ES6 syntax with reduce:

let counter = str => {
  return str.split('').reduce((total, letter) => {
    total[letter] ? total[letter]++ : total[letter] = 1;
    return total;
  }, {});
};

counter("aabsssd"); // => { a: 2, b: 1, s: 3, d: 1 }
russiansummer
  • 1,361
  • 15
  • 16
10

Another solution:

function count (string) {  
  var count = {};
  string.split('').forEach(function(s) {
     count[s] ? count[s]++ : count[s] = 1;
  });
  return count;
}
sarunast
  • 2,443
  • 3
  • 27
  • 37
9

With some ES6 features and short-circuiting:

const counter = s => [...s].reduce((a, c) => (a[c] = ++a[c] || 1) && a, {})

console.log(
  counter("hello") // {h: 1, e: 1, l: 2, o: 1}
)  
frederick99
  • 1,033
  • 11
  • 18
  • i have one query in above code you did (a[c] = a[c] + 1 || 1) it works but when i replace with a[c] = a[c]++ || 1 it should work but it does not it always count 1 for all char . Why? Could you explain? – Infinity Aug 05 '21 at 17:27
  • @Infinity x++ increments and returns the *old* value of x. so x = x++ will increment x and set it to the old value again. change it to ++x and it should work. – frederick99 Sep 03 '21 at 13:00
  • One-liners are a headache. Why would you try and obscure the nature of the code like that? – Andy Jun 04 '22 at 09:45
  • Great one-liner sir! I just expose this is a js API in my app and people don't need to care how it's implemented. – takanuva15 Oct 19 '22 at 17:02
  • @Andy agreed not to use this in "real" code. tbh, i am looking at other answers now and, with one or two exceptions, i dont think they are particularly readable either. my response to you would be not all answers are meant to be used in production :) it is there and the user can decide what to do with it. – frederick99 Nov 22 '22 at 03:37
  • @takanuva15 i hope you realized your mistake and wrote a better version haha – frederick99 Nov 22 '22 at 03:37
  • 1
    @frederick99 lol at this point I don't remember where I was using it. I think I ended up doing some refactoring and didn't need to count characters anymore so I dropped it entirely. But I don't have a big issue in general with one-liners as long as the function name is clear - a professional will understand what the purpose of the api is for based on the function name, and use it accordingly. (As with any api you would import from an 3rd-party library, whose implementation you probably don't know end-to-end anyways). – takanuva15 Nov 22 '22 at 21:39
7

Here's another way:

const freqMap = s => [...s].reduce((freq,c) => {freq[c] = -~freq[c]; return freq} ,{})

Or, if you prefer a "for" loop:

function freqMap(s) { 
   freq={}; 
   for (let c of s) 
      freq[c]=-~freq[c]; 
   return freq;
}

e.g. freqMap("MaMaMia") returns Object{M : 3, a : 3, i : 1}

This method leverages the fact that in javascript, bitwise not on "undefined" gives -1, (whereas "undefined+1" gives NaN). So, -~undefined is 1, -~1 is 2, -~2 is 3 etc.

We can thus iterate over the characters of the string, and simply increment freq[c] without any "if". The first time we encounter a character c, freq[c] will be undefined, so we set it to -~freq[c] which is 1. If we subsequently encounter c again, we again set freq[c] to -~freq[c], which will now be 2, etc.

Simple, elegant, concise.

Penny Liu
  • 15,447
  • 5
  • 79
  • 98
Yoni Rabinovitch
  • 5,171
  • 1
  • 23
  • 34
4

More declarative way to get a word histogram will be to utilise reduce to iterate through letters and come up with a new object that contains letters as keys and frequencies as values.

function getFrequency(str) {
  return str.split('').reduce( (prev, curr) => {
    prev[curr] = prev[curr] ? prev[curr] + 1 : 1;
    return prev;
  }, {});
};

console.log(getFrequency('test')); // => {t: 2, e: 1, s: 1}
vitkon
  • 1,058
  • 8
  • 13
2

a leaner, functional solution:

using ES6 Arrows && Logical Operators:

const buildFreqDict = string =>
  string.split('').reduce((freqDict, char) => {
    freqDict[char] = (freqDict[char] || 0) + 1;
    return freqDict;
  }, {})

console.log(buildFreqDict("banana"))

Explained

  • split string into array of characters.
    • and then feed it into a reduce method (using method.chaining()).
  • if char is already logged in countDict then add 1 to it.
    • or if character not found in countDict then set it to 1.
  • return new values back up to reduce's accumulator object
  • NB: don't forget about including the third argument of .reduce(): in this case it is a {} (object literal) that serves to initialize the freqDict object.

for more info see Counting instances of values in an object half way down the page here: MDN Reduce
and for more info about using logical operators please see here: MDN Logical Operators

Community
  • 1
  • 1
leerssej
  • 14,260
  • 6
  • 48
  • 57
2

An easy way. In addition, its get you an alphabetically sorted list. It loops throught an arrray and evaluate if the character is already in the object: if false, the character is added to the object, if true, its frequency increase a unit.

const text= "Lorem ipsum dolor sit amet consectetur adipiscing"
const textAsArray = text.split('').sort()
let charactersList = {}

for (char of textAsArray) {

   if (!charactersList[char]) {
    charactersList[char]=1;
   }
   else {
    charactersList[char]++
  }
}

console.log(charactersList)   
2

I have reviewed and I think that this adapts very well to the need they pose. I would like it to be in a single line but I don't know how to generate the object dynamically.

const uniqueCount=(arr)=>{
let rs ={};
arr.sort().join("").match(/(.)(\1*)/g).map(i=>rs[i[0]]=i.length);
return rs;
};
console.log(uniqueCount(["a","b","c","d","d","e","a","b","c","f","g","h","h","h","e","a"]));
//{ a: 3, b: 2, c: 2, d: 2, e: 2, f: 1, g: 1, h: 3 }

I find it very successful to use .match() and regex /(.)(\1*)/g as explained above.

If it is just a string, you just need to add a .split("") before and that's it.

Jasp402
  • 402
  • 3
  • 12
2

One more version with sorting by alphabetically. This function works for both.

  1. Frequency of characters by alphabetically sorted
  2. Frequency of characters in order of occurrence

Caveat: Only works if whole string is in lowercase

function freqWithAlphabetTable(str, doNeedToSort) {
    let cnt = new Array(26).fill(0), firstLowerCase = 97, output = {}
    for (let i = 0; i < str.length; i++)
        cnt[str[i].charCodeAt(0) - firstLowerCase]++ // filling the array with count at it's index
    if (doNeedToSort) {
        for (let i = 0; i < cnt.length; i++) {
            if (cnt[i] !== 0)
                output[String.fromCharCode(firstLowerCase)] = cnt[i]
            firstLowerCase++;
        }
    } else {
        for (let i = 0; i < str.length; i++) {
            let letterIndexVal = cnt[str[i].charCodeAt(0) - firstLowerCase];
            if (letterIndexVal != 0 ) {
                output[str[i]] = letterIndexVal
                letterIndexVal = 0 // replacing it with zero to avoid repetition
            }
        }
    }
    console.log(output);
    return output;
}
Alok Deshwal
  • 1,128
  • 9
  • 20
1

Here's another option using underscore.js:

function charCount(str) {
    return _(str.split('')).countBy(function(char) {
        return char.toLowerCase();
    });
}

charCount('aaabbbbdd') outputs Object {a: 3, b: 4, d: 2}

colllin
  • 9,442
  • 9
  • 49
  • 65
1
for(i = strlen(string)var string = 'aabsssd';
var chars = new Array();
for(var i = 0; i < string.length; i++){
    var char = string.charAt(i);
    if(chars[char] == undefined){
        chars[char] = 0;
    }
    chars[char]++;
}
console.log(chars);
Andy
  • 2,892
  • 2
  • 26
  • 33
1

 const recorrences = ['a', 'b', 'c', 'a', 'b','a']
                .map(i => !!~i.indexOf('a'))
                .filter(i => i)
                .length;
console.log(`recorrences ${recorrences}`) 
//recorrences 3
Erick Wendel
  • 402
  • 6
  • 14
1
// Count frequency of characters in a string
// input: 'Hello, I'm Paul!'
// result: {
//      H: 1,
//      E: 1,
//      L: 3,
//      ... and so on ...
// }

const countChars = (string) => {
    let charStats = {};
    string = string.replace(' ', '').toUpperCase().split('');

    string.forEach((char) => {
        if (charStats[char]) {
            charStats[char]++;
        } else {
            charStats[char] = 1;
        }
    });

    return charStats;
};
Sergei Volynkin
  • 401
  • 4
  • 10
1

Another Solution

    function maxChar(str) {

        const charMap = {};
        let max = 0;
        let maxChar = '';

        for(let char of str){
            if(charMap[char]){
                charMap[char]++;
            }else{
                charMap[char] = 1;
            }
        }

        for(let char in charMap){
            if(charMap[char] > max){
                max = charMap[char];
                maxChar = char;
            }
        }

        return maxChar; 
}

===>

 maxChar('355385') 
  "5"
bajran
  • 1,433
  • 14
  • 23
1
var str = 'abcccdddd';

function maxCharCount(target) {
    const chars = {};

    let maxChar = '';
    let maxValue = 1;

    for (let char of target) {
        chars[char] = chars[char] + 1 || 1;
    }

    return chars;
}

console.log(maxCharCount(str));
Aaron
  • 2,364
  • 2
  • 31
  • 56
1

The same solution but refactored. So cool how we can solve this problem with so many different answers :)

function getFrequency(string) {

    var freq = {};

    for (let character in string) {

         let char = string[character];        
         (freq[char]) ? freq[char]++ : freq[char] = 1

    }

    return freq;

};
Leo Garza
  • 41
  • 5
1

You can use this. Just pass the string and it will return object with all the character frequency.

function buildCharMap(string) {
  const charMap = {};
  string.replace(/[^\w]/g, '').toLowerCase();
  for (let char of string) {
    charMap[char] = charMap[char] + 1 || 1;
  }
  return charMap;
}
Sahil Jain
  • 169
  • 1
  • 8
1

cheat code to count frequency of a char in a string is

let target = "e";
let string = " i want to see that person that came in here last";
let frequency = string.split(target).length - 1;

or all in one line

console.log(string.split("e").length - 1)
JesusBoy
  • 39
  • 4
1
 [...str].map( char => map.get(char) ? map.set( char, map.get(char) + 1) : map.set(char,1) ) 
saeed eivazi
  • 818
  • 1
  • 7
  • 14
  • Although this answer addresses the question, I would improve a lot if you could edit it and add an explanation on how that code helps – Cleptus Oct 26 '20 at 13:34
1

Everyone using split and reduce are over-complicating things.

string is an iterator so you can use a for/of loop to go over each letter - there's no need to split it into an array so reduce can use it. reduce is very useful for lots of things but it often seems like: "when all you have is a hammer everything looks like a nail". I think its used unnecessarily in many places.

Anyway...

  1. Create a new object.
  2. Loop over the string.
  3. If there is no key in the object that corresponds to the current letter, add it and set it it to zero.
  4. Increment it.

function counter(str) {

  // Create an object
  const obj = {};

  // Loop through the string
  for (const letter of str) {

    // If the object doesn't have a `letter`
    // property create one and set it to 0;
    obj[letter] ??= 0;

    // Increment the value
    ++obj[letter];

  }

  // Finally return the object
  return obj;

}

const str = 'aabbddd';
console.log(counter(str));

Additional documentation

Andy
  • 61,948
  • 13
  • 68
  • 95