2

Been stuck on this problem for a bit and need help:

Given an string, count how many times each letter occurs inside a given string; the letters must stay in order of the way the appear in the string with the count next to them. The return must be a single string with letters and count.

Example:

for example: "apple" is "a1p2l1e1", "tees" is "t1e2s1"

I got kinda close with the output format but not quite? this is my code:

function countLetters(str){
    let newArr = []
    let i = 0;
    while(i < str.length){
        if(str.includes(str[i])){
            newArr += str[i];
            newArr += 1;
            i++;
    }
    
}
return newArr;
}; 
console.log(countLetters("apple")); // "a1p2l1e1"

and this is my output:

"a1p1p1l1e1"

Any ideas?

4 Answers4

1

You could use a pattern with a capture group and an optionally repeated backreference \1* to repeat matching the same char of the group.

In the callback of replace, print group 1 which hold the character and concatenate the length of the full match.

The pattern (\S)\1* matches

  • (\S) Capture group 1, match a single non whitespace char
  • \1* Backreference to group 1 and repeat 0+ times to also count a single occurrence

const countLetters = s => s.replace(/(\S)\1*/g, (m, g1) => g1 + m.length);

["apple", "tees"].forEach(s => console.log(countLetters(s)));

Edit

If you want also want to count non adjacent characters in the order you encounter them, you can use a map, increment the value for every key using 1 as a default value and at the end concat the keys and the values.

const countLetters = function(str) {
  const m = new Map;
  let result = "";

  for (let c of str) {
    m.has(c) ? m.set(c, m.get(c) + 1) : m.set(c, 1);
  }
  m.forEach((v, k) => result += k + v);
  return result;
};

["apple", "tees", "banana", "coconut"].forEach(s => console.log(countLetters(s)));
The fourth bird
  • 154,723
  • 16
  • 55
  • 70
  • 1
    this is very interesting! I'm still a super beginner so I haven't heard of a capture group or backreference yet. I'll look into these, thanks! – Mirii_Made_Code Jan 30 '21 at 18:58
  • thanks I can do that. I did drag this into VS code to play around with some test cases. It looks like it didn't print "banana" and "coconut" correctly. The test cases should've returned respectfully as "b1a3n2" and "c2o2n1u1t1" Do you know why that would be? – Mirii_Made_Code Jan 30 '21 at 19:32
  • 1
    @Mirii_Made_Code I have added an example approach of how you might do that. – The fourth bird Jan 30 '21 at 19:58
  • 1
    OMG Thank you! this is very helpful to look at it like this. – Mirii_Made_Code Jan 30 '21 at 20:18
0

Minimalist approach.

Set allows you to store unique values of any type, Split is the easiest approach to counting char occurrences. If you want better performance checkout: Count the number of occurrences of a character in a string in Javascript

const word = "apple";

let result = "";

[...new Set(word)] // or Array.from(new Set(word))
.forEach((letter) => {
    const frequency = word.split(letter).length - 1;
    result += `${letter}${frequency}`;
});

console.log(result); // a1p2l1e1
Filip Seman
  • 1,252
  • 2
  • 15
  • 22
  • change `[...new Set(word))]` into `[...new Set(word)]`.. there's a syntax error when I try to run the code – The Bomb Squad Jan 29 '21 at 22:34
  • Thanks I had originally looked at that post but it seems to be for counting single character within a string not counting not necessarily counting all occurrences of all character of the string. Set looks useful though. – Mirii_Made_Code Jan 30 '21 at 19:07
0

The following code seems to work:

function countLetters(str){
  let newArr   = [];
  let i        = 1;
  let lastChar = str[0];
  let count     = 1;

  while(i < str.length) {

    if((str[i]!==lastChar)) {  //if it encounters new character
      newArr += lastChar;
      newArr += count
      lastChar = str[i];
      count=1;
    }
    else {
      count++;
    }
    i++;
  }
  //for the last character
  newArr += lastChar;
  newArr += count
  lastChar = str[i];
  return newArr;
}
console.log(countLetters("apple"));   // a1p2l1e1
console.log(countLetters("banana"));  // b1a1n1a1n1a1
Mister Jojo
  • 20,093
  • 6
  • 21
  • 40
Arpan Sur
  • 11
  • 2
  • this is a big chunk of code, I'm not sure I can provide this genre today ;) – Mister Jojo Jan 29 '21 at 23:15
  • this looks very similar to another version of the answer I was trying to do..but couldn't quite get the control flow to work on. However this code doesnt pass the second test case. "banana" should be "b1a3n2" not "b1a1n1a1n1a1" – Mirii_Made_Code Jan 30 '21 at 19:17
  • 1
    @Mirii_Made_Code: I tried to remain true to the approach you were already following. However, I cannot think of a way to handle the 'banana' scenario in this approach unless we use a map. I see that there already is an answer using map. This and the solution using map can be combined if you are looking for a verbose solution. – Arpan Sur Feb 01 '21 at 07:06
  • thank you!!! Yes it seems the map works. Also that is good to know that it would be difficult to do without one. – Mirii_Made_Code Feb 03 '21 at 00:02
0

Respecting your question
<< the letters must remain in the order of their appearance in the chain with the count next to them >>

According to the post from The Bomb Squad (unanswered for the moment)

const countLetters = str =>
  [...str].reduce((a,Lc,i,{[i+1]:Ln}) => 
    {
    if (Lc != Ln) { a.r += `${Lc}${++a.n}`; a.n = 0 }
    else          a.n++
    return !!Ln ? a : a.r
    }
    ,{r:'',n:0})

console.log('apple ->', countLetters('apple'));   // 'a1p2l1e1'
console.log('banana ->', countLetters('banana')); // 'b1a1n1a1n1a1'
.as-console-wrapper { max-height: 100% !important; top: 0; }
Mister Jojo
  • 20,093
  • 6
  • 21
  • 40