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));

6
  • 4
    Have you tried anything? Could you provide us a sample of your code? Commented Sep 4, 2013 at 17:03
  • possible duplicate stackoverflow.com/questions/4009756/… Commented Sep 4, 2013 at 17:04
  • Please show your attempts to solve the problem, and then answers can directly critique the code you provide. Commented Sep 4, 2013 at 17:04
  • I am trying here: jsfiddle.net/creativevilla/wjD2r Commented Sep 4, 2013 at 17:11
  • @Biswajit: Its not duplicate. I am trying to get occurrence of every letter. Commented Sep 4, 2013 at 17:12

21 Answers 21

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;
};
Sign up to request clarification or add additional context in comments.

3 Comments

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
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
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?
24

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 }

Comments

10

Another solution:

function count (string) {  
  var count = {};
  string.split('').forEach(function(s) {
     count[s] ? count[s]++ : count[s] = 1;
  });
  return count;
}

Comments

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}
)  

7 Comments

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 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.
One-liners are a headache. Why would you try and obscure the nature of the code like that?
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.
@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).
|
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.

Comments

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}

Comments

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

Comments

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)   

Comments

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.

Comments

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;
}

Comments

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);

Comments

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}

Comments

1

 const recorrences = ['a', 'b', 'c', 'a', 'b','a']
                .map(i => !!~i.indexOf('a'))
                .filter(i => i)
                .length;
console.log(`recorrences ${recorrences}`) 
//recorrences 3

Comments

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;
};

Comments

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"

Comments

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));

Comments

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;

};

Comments

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;
}

Comments

1
 [...str].map( char => map.get(char) ? map.set( char, map.get(char) + 1) : map.set(char,1) ) 

1 Comment

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
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)

2 Comments

This is actually false and return 7 instead of 6
@JorelAmthor just editted my answer , always minus 1from length, and look out for -1, meaning no matches
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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.