JavaScript function to automatically count consecutive letters in a string
Asked Answered
J

6

6

I am attempting (unsuccessfully) to write JavaScript function LetterCount to count the consecutive letters in a string (and not the total number).

Ideally: LetterCount("eeeeeoooohhoooee") = [["e", 5],["o",3],["h",2],["o",3],["e",2]]

The following code attempts to count the number of consecutive letters in a string only when I already know what they are:

function LetterCount(str) {
for (var i=0; i<str.length;i++) {
    var arr1=[]; arr2=[]; arr3=[]; arr4=[]; arr5=[];
    var e=0; o=0; h=0; o2=0; e2=0;
    if(str[i]="e") {
        arr1 += "e";
        e++;
    }
    arr1.push(e);
    if(str[i]="o") {
        arr2 += "o";
        o++;
    }
    arr2.push(o);
    if(str[i]="h") {
        arr3 += "h";
        h++;
    }
    arr3.push(h);
    if(str[i]="o") {
        arr4 += "o";
        o2++;
    }
    arr4.push(o2);
    if(str[i]="e") {
        arr5 += "e";
        e2++;
    }
    arr5.push(e2);
}
return arr1.concat(arr2).concat(arr3).concat(arr4).concat(arr5);
}

In the code above, I need to first know what the letters in the string are, and how many of them are present, in what order.

INSTEAD: How do you write a function that will automatically recognize the letter themselves, and then return the count of consecutive letters. Would also be great if the answer is in the following format:

 LetterCount("eeeeeoooohhoooee") = [["e", 5],["o",3],["h",2],["o",3],["e",2]]

Any help is much appreciated!

Jasminejason answered 22/2, 2015 at 3:15 Comment(0)
E
18

You can use a regular expression to match any letter followed by zero or more instances of the same letter.

rx=/([a-zA-Z])\1*/g;

Your example matches ["eeeee","oooo","hh","ooo","ee"].

Using map, return the initial letter and the number of occurrences in a new array for each index.

function letterCount(str) {
  var s = str.match(/([a-zA-Z])\1*/g) || [];
  return s.map(function(itm) {
    return [itm.charAt(0), itm.length];
  });
}

console.log(letterCount("eeeeeoooohhoooee"))

returned value: (Array)

[["e",5],["o",4],["h",2],["o",3],["e",2]]

NOTES:

  1. var s= str.match(/([a-zA-Z])\1/g)||[];*

returns an array of matches (repeated letters) or an empty array([]). Otherwise, if the string does not contain any letters an error would be thrown (from calling map on null).

  1. \1* is used to allow matching instances of a single letter with any or no sequential repetition. '\1+' would not match a single unrepeated letter.

  2. Array map expects a function and passes three arguments- the value at each index, the index number, and a reference to the entire array. In this case, only the value of each index is used, so we can ignore the other arguments.

Eudosia answered 22/2, 2015 at 3:59 Comment(6)
I like this for simplicity and the use of regex as that will most likely result in much faster processing. Also I have never used map before. Much better than my answer :)Helluva
If anything could possibly be improved, it would probably be to create an additional enclosure so that the anonymous function isn't declared at each runtime. But even that would take away from the simplicity of it all.Helluva
Thank you both for this beautiful solution! What is the different between matching ([a-zA-Z])\1*/g) and ([a-zA-Z]\1+/g) on the second line? Isn't the asterisk or star * used when the preceding character occurs 0 or more times, while in this case it should be 1 or more times, i.e. used with a plus sign +?Jasminejason
What does "itm" stand for? Essentially, does the 2nd line read: s will EITHER be the match method on the given string, OR an empty array, if no matches are found? Thanks so much for your clarification.Jasminejason
I'm explaining someone else's code but; The star is used because the algorithm is targeting zero or more instances of the same character consecutively, required to match cases where there is no consecutive same-character sequence. The statement var s= str.match(/([a-zA-Z])\1*/g)||[]; basically says if str.match(...) returns a falsy value such as 0, null, or undefined to assign an empty array to s instead of what was returned by str.match.Helluva
@Jasminejason Sorry I forgot to tag you.. Anyways the explanation for the use of || in JavaScript is that it doesn't actually work like an "or" statement, but rather it will just keep executing until it encounters a truthy (or rather non-falsy) value to return. If it can't find one it will return false (or at least a falsy value). && on the other hand works exactly as expected and will return either true or false.Helluva
H
2

This is my answer:

function LetterCount(str) {
    var current, i = 0, l = str.length;
    var outputArr = [];
    while(i<l) {
        current = str.charAt(i);
        if(!i++ || outputArr[outputArr.length-1][0] !== current)
            outputArr[outputArr.length] = [current, 1];
        else outputArr[outputArr.length-1][1]++;
        }
    return outputArr;
    }

As a modification to kennebec's (awesome) answer, so that the anonymous function isn't declared each time the parent function is called. This is only to reference a better programming practice in comparison to pure simplicity (this is probably the most efficient method):

var letterCount = (function(){
    var r = /([A-z])\1*/g,
        f = function(itm){
        return [itm.charAt(0), itm.length];
        };
    return function letterCount(str){
        return str.match(r).map(f);
        };
    }());
Helluva answered 22/2, 2015 at 3:36 Comment(0)
H
2

Actually "fixed" ["o",3] to ["o",4] ;)

// node v0.10.31
// assert v1.3.0

var assert = require('assert');

function letterCount(str) {
    var i = 0,
        seq = 0,
        results = [];

    while (i < str.length) {
        var current = str[i],
            next = str[i + 1];

        if (typeof results[seq] === 'undefined') {
            results[seq] = [current, 0];
        }

        results[seq][1]++;

        if (current !== next) {
            seq++;
        }

        i++;
    }

    return results;
}

var actual = letterCount('eeeeeoooohhoooee');
var expected = [["e", 5],["o",4],["h",2],["o",3],["e",2]];

assert.deepEqual(actual, expected);
Huth answered 22/2, 2015 at 3:40 Comment(0)
R
1

I'd use a map keyed on the character to store the count of consecutive chars and then build the output structure at the end. I'm not sure I understand exactly what you mean by consecutive based on your example but you can tweak the logic to identify a consecutive number accordingly.

function LetterCount(str) {
  var counts = {};
  for (var i = 0, prevChar = null; i < str.length; i++) {
    var char = str.charAt(i);
    if(counts.hasOwnProperty(char) && char === prevChar) {
      counts[char] = counts[char] + 1;  
    } else if (!counts.hasOwnProperty(char)) {
      counts[char] = 0;
    }
    prevChar = char;
  }
  var res = [];
  for (var char in counts) {
    if (counts.hasOwnProperty(char)) {
      res.push([char,counts[char]);
    }
  }
  return res;
}
Rozella answered 22/2, 2015 at 3:29 Comment(0)
O
0
function LetterCount(text){
    arr = [];
    letter = text[0];
    counter = 0;
    for (c in text+' '){
        if (text[c] != letter){
            newArr = [letter, counter];
            arr.push(newArr);
            letter = text[c];
            counter = 0;
        }
        counter += 1;
    };
    return arr;
}
Oaf answered 22/2, 2015 at 3:33 Comment(0)
P
0

const string = 'acbbaeekzzkeee';

function letterCount(str) {
  return [...str].reduce((acc, letter, index) => {
    if (index === 0 || letter !== str[index - 1]) {
      acc.push([letter, 1]);
    } else {
      acc[acc.length - 1][1]++;
    }
    return acc;
  }, []);
}

const count = letterCount(string);
console.log(count);
  1. ...spread string into array
  2. Loop through it with reduce, which initial value is empty array
  3. if it is first letter or previous letter is not same as letter
  4. Make an array with [letter, 1] and push it into accumulator array (1 is initial count/value)
  5. else increment previous array's value in accumulator's array. Since previous array will have same letter as our current letter
  • So whenever we have new value (non-consecutively) we add an array into array.
  • Else we increment last array's value (it will be same letter)
Packthread answered 24/2, 2023 at 18:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.