Counting frequency of character

Hello everyone,

I was thinking of the pseudo-code to count the frequency of characters in text. It was getting too long and I found this functions that do it, however, they are not perfect, but I’m not sure how to fix it. I’d appreciate if someone can walk me through the code. I’d like to write my own, but would be helpful to compare with other examples.

Number 1:

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

Number 2

function getFrequency(text) {
 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;
};

My pseudo code, with too many indentation levels:

function countLetters(text)
   count = {}
   if text.length > 2 
      previousLetter = text.ChartAt[0];
      count = letter, 1  // how do I write this in js?
      for (ch.chartAt[1], ch < text.length - 1, ch++) 
         nextCharacter = text(ch)
            if new character in count object
               count = ch +1
            else 
               add new ch count in count
     return count
     else
         count = ch, 1

Thanks a lot.

1 Like

What’s wrong with the code in the first example you listed - it does exactly what you said you want to do.

Pseudo code would be:

1. Create an empty object.
2. For each letter in the text 
    2a. If the letter is not a property of the object then add it with value=1
    2b. If the letter is already a property of the object increment the count
3. Return the object containing the letters and their frequencies
2 Likes

thanks! As for the first function, I get string is not defined. And I’m not sure how to initialise it.

1 Like

I missed seeing that error.

It should be text.split not String.split

1 Like

still, none of them pass tests:

Get letter frequency - basic tests
Resulting array is wrong - Expected: [[“v”,5],[“h”,4],[“d”,2],[“l”,2],[“w”,2],[“f”,1],[“j”,1],[“k”,1],[“p”,1],[“u”,1]], instead got: {“w”:2,“k”:1,“l”:2,“v”:5," “:4,“d”:2,“h”:4,“f”:1,“u”:1,“p”:1,“j”:1}
Resulting array is wrong - Expected: [[“i”,7],[“a”,5],[“e”,5],[“n”,5],[“g”,4],[“s”,4],[“m”,3],[“o”,3],[“t”,3],[“d”,2],[“l”,2],[“r”,2],[“c”,1],[“f”,1],[“h”,1],[“k”,1],[“u”,1],[“y”,1]], instead got: {“A”:1,“s”:4,” “:14,“l”:2,“o”:2,“n”:5,“g”:4,“a”:4,“I”:3,”'“:3,“m”:3,“e”:5,“r”:2,“i”:4,“t”:3,“h”:1,”,“:1,“f”:1,“u”:1,“O”:1,“K”:1,”-“:1,“d”:2,“c”:1,“y”:1,”.“:1}
Resulting array is wrong - Expected: [[“t”,12],[“i”,7],[“h”,6],[“a”,5],[“g”,5],[“p”,5],[“x”,5],[“d”,4],[“s”,4],[“u”,4],[“e”,3],[“r”,3],[“b”,2],[“c”,2],[“l”,2],[“n”,1],[“w”,1],[“z”,1]], instead got: {“I”:7,“W”:1,“T”:12,” “:17,“L”:2,“D”:4,“G”:5,“A”:5,“S”:4,“X”:5,“H”:6,“P”:5,“E”:3,“R”:3,”,“:1,“B”:2,“N”:1,“U”:4,“Z”:1,“C”:2,”.":1}

By the way, this is what they’re testing:

Test.describe(“Get letter frequency - basic tests”,function(){

result = letterFrequency(‘wklv lv d vhfuhw phvvdjh’);
expected = [[‘v’, 5], [‘h’, 4], [‘d’, 2], [‘l’, 2], [‘w’, 2], [‘f’, 1], [‘j’, 1], [‘k’, 1], [‘p’, 1], [‘u’, 1]];

Test.assertSimilar(result, expected, “Resulting array is wrong”);

result = letterFrequency(“As long as I’m learning something, I figure I’m OK - it’s a decent day.”);
expected = [[“i”, 7], [“a”, 5], [“e”, 5], [“n”, 5], [“g”, 4], [“s”, 4], [“m”, 3], [“o”, 3], [“t”, 3], [“d”, 2], [“l”, 2], [“r”, 2], [“c”, 1], [“f”, 1], [“h”, 1], [“k”, 1], [“u”, 1], [“y”, 1]];

Test.assertSimilar(result, expected, “Resulting array is wrong”);

result = letterFrequency(‘IWT LDGAS XH HIXAA P LTXGS EAPRT, STHEXIT BN TUUDGIH ID BPZT RATPG PCS ETGUTRI HTCHT DU XI.’);
expected = [[“t”, 12], [“i”, 7], [“h”, 6], [“a”, 5], [“g”, 5], [“p”, 5], [“x”, 5], [“d”, 4], [“s”, 4], [“u”, 4], [“e”, 3], [“r”, 3], [“b”, 2], [“c”, 2], [“l”, 2], [“n”, 1], [“w”, 1], [“z”, 1]];

Test.assertSimilar(result, expected, “Resulting array is wrong”);

})

I’d prefer to translate your pseudo code.

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

console.log(letterFrequency('wklv lv d vhfuhw phvvdjh'));

gives: {’ ': 4, ‘d’: 2, ‘f’: 1, ‘h’: 4, ‘j’: 1, ‘k’: 1, ‘l’: 2, ‘p’: 1, ‘u’: 1, ‘v’: 5, ‘w’: 2}

which appears to be the correct result for that input and that function.

Note that the code inside the forEach was also slightly incorrect in your original.

1 Like

but a white space is not a letter.

and again, it doesn’t pass the tests:

Get letter frequency - basic tests
Resulting array is wrong - Expected: [[“v”,5],[“h”,4],[“d”,2],[“l”,2],[“w”,2],[“f”,1],[“j”,1],[“k”,1],[“p”,1],[“u”,1]], instead got: {“w”:2,“k”:1,“l”:2,“v”:5," “:4,“d”:2,“h”:4,“f”:1,“u”:1,“p”:1,“j”:1}
Resulting array is wrong - Expected: [[“i”,7],[“a”,5],[“e”,5],[“n”,5],[“g”,4],[“s”,4],[“m”,3],[“o”,3],[“t”,3],[“d”,2],[“l”,2],[“r”,2],[“c”,1],[“f”,1],[“h”,1],[“k”,1],[“u”,1],[“y”,1]], instead got: {“A”:1,“s”:4,” “:14,“l”:2,“o”:2,“n”:5,“g”:4,“a”:4,“I”:3,”'“:3,“m”:3,“e”:5,“r”:2,“i”:4,“t”:3,“h”:1,”,“:1,“f”:1,“u”:1,“O”:1,“K”:1,”-“:1,“d”:2,“c”:1,“y”:1,”.“:1}
Resulting array is wrong - Expected: [[“t”,12],[“i”,7],[“h”,6],[“a”,5],[“g”,5],[“p”,5],[“x”,5],[“d”,4],[“s”,4],[“u”,4],[“e”,3],[“r”,3],[“b”,2],[“c”,2],[“l”,2],[“n”,1],[“w”,1],[“z”,1]], instead got: {“I”:7,“W”:1,“T”:12,” “:17,“L”:2,“D”:4,“G”:5,“A”:5,“S”:4,“X”:5,“H”:6,“P”:5,“E”:3,“R”:3,”,“:1,“B”:2,“N”:1,“U”:4,“Z”:1,“C”:2,”.":1}

You appear to have two extra conditions that weren’t clear in your original.

  1. that only letters of the alphabet be counted and
  2. that the case of the letters not matter.

Adding those extra conditions into the code gives:

function letterFrequency(text){
 var count = {};
 text.split('').filter(function(s) {
   return s.match(/[a-z]/i); // get rid of anything not a letter
 }).forEach(function(s) {
   s = s.toLowerCase(); // convert to lowercase
   count[s] = count[s] ? count[s]+1 : 1; // add to count for that letter
 });
 return count;
}

console.log(letterFrequency("As long as I'm learning something, I figure I'm OK - it's a decent day"));

resulting in {a: 5, c: 1, d: 2, e: 5, f: 1, g: 4, h: 1, i: 7, k: 1, l: 2, m: 3, n: 5, o: 3, r: 2, s: 4, t: 3, u: 1, y: 1}

1 Like

it’s not passing the tests again…

Get letter frequency - basic tests
Resulting array is wrong - Expected: [[“v”,5],[“h”,4],[“d”,2],[“l”,2],[“w”,2],[“f”,1],[“j”,1],[“k”,1],[“p”,1],[“u”,1]], instead got: {“w”:2,“k”:1,“l”:2,“v”:5,“d”:2,“h”:4,“f”:1,“u”:1,“p”:1,“j”:1}
Resulting array is wrong - Expected: [[“i”,7],[“a”,5],[“e”,5],[“n”,5],[“g”,4],[“s”,4],[“m”,3],[“o”,3],[“t”,3],[“d”,2],[“l”,2],[“r”,2],[“c”,1],[“f”,1],[“h”,1],[“k”,1],[“u”,1],[“y”,1]], instead got: {“a”:5,“s”:4,“l”:2,“o”:3,“n”:5,“g”:4,“i”:7,“m”:3,“e”:5,“r”:2,“t”:3,“h”:1,“f”:1,“u”:1,“k”:1,“d”:2,“c”:1,“y”:1}
Resulting array is wrong - Expected: [[“t”,12],[“i”,7],[“h”,6],[“a”,5],[“g”,5],[“p”,5],[“x”,5],[“d”,4],[“s”,4],[“u”,4],[“e”,3],[“r”,3],[“b”,2],[“c”,2],[“l”,2],[“n”,1],[“w”,1],[“z”,1]], instead got: {“i”:7,“w”:1,“t”:12,“l”:2,“d”:4,“g”:5,“a”:5,“s”:4,“x”:5,“h”:6,“p”:5,“e”:3,“r”:3,“b”:2,“n”:1,“u”:4,“z”:1,“c”:2}

i copy the explanation - may be i should have done this earlier, but couldn’t see anything special here. This is an exercise called character frequency.

Write a function that takes a piece of text in the form of a string and returns the letter frequency count for the text. This count excludes numbers, spaces and all punctuation marks. Upper and lower case versions of a character are equivalent and the result should all be in lowercase.

The function should return a list of tuples (in Python) or arrays (in other languages) sorted by the most frequent letters first. Letters with the same frequency are ordered alphabetically. For example:

letterFrequency(‘aaAabb dddDD hhcc’)
will return

[[‘d’,5], [‘a’,4], [‘b’,2], [‘c’,2], [‘h’,2]]
Letter frequency analysis is often used to analyse simple substitution cipher texts like those created by the Caesar cipher.

You just need to convert the object keys into array items.

The following addition will do the job, where letterFrequencyArray(‘Some text’) results in:
[ [‘e’, 2], [‘t’, 2], [‘m’, 1], [‘o’, 1], [‘s’, 1], [‘x’, 1] ]

function convertKeysToItems(obj) {
    var itemisedArray = [];
    Object.keys(obj).forEach(function (key) {
        var item = [];
        item.push(key);
        item.push(obj[key]);
        itemisedArray.push(item);
    });
    return itemisedArray;
}

function sortByNumThenLetter(array) {
    return array.sort(function (a, b) {
        var firstLetter = a[0],
            firstNum = a[1],
            secondLetter = b[0],
            secondNum = b[1];
        if (firstNum < secondNum) {
            return 1;
        }
        if (firstNum > secondNum) {
            return -1;
        }
        if (firstLetter > secondLetter) {
            return 1;
        }
        if (firstLetter < secondLetter) {
            return -1;
        }
        return 0;
    });
}

function letterFrequencyArray(text) {
    var freq = letterFrequency(text),
        items = convertKeysToItems(freq);
    return sortByNumThenLetter(items);
}

Some example code for this can be found at https://jsfiddle.net/29d5hsuf/1/

I am saddened though that you haven’t learned much about coding. Having other people do the work for you tends to result in failure for you further down the track.

1 Like

The extra criteria you have now introduced can be dealt with by changing what we return from the function which isn’t that much extra code.

function letterFrequency(text){
 var count = {};
 text.split('').filter(function(s) {
   return s.match(/[a-z]/i); // get rid of anything not a letter
 }).forEach(function(s) {
   s = s.toLowerCase(); // convert to lowercase
   count[s] = count[s] ? count[s]+1 : 1; // add to count for that letter
 });
  return Array.from(Object.keys(count),function(n) {
                    return [n,count[n]];}).sort(function(a,b) {
                    return a[1]<b[1]? 1:a[1]>b[1]?-1:a[0]>b[0]? 1:a[0]<b[0]?-1:0;}); 
}

console.log(letterFrequency(
     "As long as I'm learning something, I figure I'm OK - it's a decent day"));

Easier done with map rather than forEach (and Array.from will take a map function as the second parameter)

In your separate function you could just do

function convertKeysToItems(obj) {
return Object.keys(obj).map(function (key) {return [key,obj[key];});
}

Perhaps they learnt something about how important all those parts of the requirement they thought were insignificant are in determining the code required. Their original request only mentioned one of at least five separate criteria that all needed significant code changes.

Thanks. Well I did a Javascript course a couple of years ago and I started yesterday to refresh what I’ve learned. The course didn’t include objects to instead teach JQuery, something that I didn’t think it was a good option.
Before refreshing JS I was practising with Python. I have a good book Python for Everyone, with tons of exercises. Although there’s a jump between the theory and the practice.
I wish there was a very good book to learn JS, something like a mix of Head First JS, with Python for Everyone but including a step by step approach that everyone needs for learning.
I have some of the Sitepoint books, but for example JS Novice put me off after they start with that .core file that throws away the step by step process by including something that it’s not explained and that makes it impossible to reproduce as from then on, one needs that .core file.
Another very good model for a complete book would be Jump Start JS, but covering the whole of the language like that complete reference by O’Reilly: JS The Definite Guide. It could be written collaboratively by different authors.
Books sometime have non authentic examples: like an object with dog.bark etc…Anyway, it seems that programmers need to include someone who knows about education for producing books. Hope Sitepoint can evolve in this direction.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.