How to Count Characters in a Javascript String


The challenge

The main idea is to count all the occurring characters in a string. If you have a string like aba, then the result should be {'a': 2, 'b': 1}.

What if the string is empty? Then the result should be an empty object literal, {}.

The solution in Javascript

Option 1:

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

Option 2:

function count (string) {
  return string.split('').reduce(function(counts,char){
    counts[char] = (counts[char]||0) + 1;
    return counts;
  },{});
}

Option 3:

const count = string =>
  [...string].reduce((pre, val) => (pre[val] = -~pre[val], pre), {});

Test cases to validate our solution

const chai = require("chai");
const assert = chai.assert;
chai.config.truncateThreshold=0;

describe("Count characters", function(){
    function objectEqual( x, y ) {
      if ( x === y ) return true;
        // if both x and y are null or undefined and exactly the same
    
      if ( ! ( x instanceof Object ) || ! ( y instanceof Object ) ) return false;
        // if they are not strictly equal, they both need to be Objects
    
      if ( x.constructor !== y.constructor ) return false;
        // they must have the exact same prototype chain, the closest we can do is
        // test there constructor.
    
      for ( let p in x ) {
        if ( ! x.hasOwnProperty( p ) ) continue;
          // other properties were tested using x.constructor === y.constructor
    
        if ( ! y.hasOwnProperty( p ) ) return false;
          // allows to compare x[ p ] and y[ p ] when set to undefined
    
        if ( x[ p ] === y[ p ] ) continue;
          // if they have the same strict value or identity then they are equal
    
        if ( typeof( x[ p ] ) !== "object" ) return false;
          // Numbers, Strings, Functions, Booleans must be strictly equal
    
        if ( ! Object.equals( x[ p ],  y[ p ] ) ) return false;
          // Objects and Arrays must be tested recursively
      }
    
      for ( p in y ) {
        if ( y.hasOwnProperty( p ) && ! x.hasOwnProperty( p ) ) return false;
          // allows x[ p ] to be set to undefined
      }
      return true;
    }
    
    it ("should give empty object literal if string is empty", function(){
      //Test.assertEquals(true, objectEqual(count(""), {}));
      assert.deepEqual(count(""), {});
    });
    
    it ("should get as a result two A characters", function () {
      //Test.assertEquals(true, objectEqual(count("aa"), { a: 2 })); 
      assert.deepEqual(count("aa"), { a: 2 }); 
    });
    
    it ("should get as a result of two a characters and two b characters", function () {
      //Test.assertEquals(true, objectEqual(count("aabb"), { a: 2, b: 2 })); 
      assert.deepEqual(count("aabb"), { a: 2, b: 2 }); 
    });
    
    it ("should get as a result of two a characters and two b characters, showing that the result order does not matter", function () {
      //Test.assertEquals(true, objectEqual(count("aabb"), { b: 2, a: 2 })); 
      assert.deepEqual(count("aabb"), { b: 2, a: 2 }); 
    });
    
    it("should check for adding varying number of characters, random number of a's and b's", function () {
      
      let word = "", a = 0, b = 0;
      
      for (; a < Math.random() * 100; a++) {
        word += "a";
      }

      for (; b < Math.random() * 100; b++) {
        word += "b";
      }
    
      //Test.assertEquals(true, objectEqual(count(word), { "a": a, "b": b })); 
      assert.deepEqual(count(word), { "a": a, "b": b });
    });
});