# Run-length coder

I have this code, which counts only adjacent letters in a string and returns the count of each letter.

const inputData = [
'aabbc',
'xxxxvvhjh',
'PPPOM'
];
let output = [];
inputData.forEach(str => {
let strObj = {};
for (let i = 0; i < str.length; i++) {
if (strObj[str[i]] && str[i] === str[i - 1]) {
strObj[str[i]] = strObj[str[i]] += 1;
} else {
strObj[str[i]] = 1;
}
}
const res = Object.keys(strObj).map((key) => ${key}${strObj[key]}).join('');
output.push(res);
});
console.log(output);


Output

[ 'a2b2c1','x4v2h1j1h1','P3O1M1']


The observation by George is correct- it does iterate over the inputs twice. The prescribed approach seems to work (see demonstration at the end of the post).

# Suggestions

## line to increment count- extra assignment

This line looks like it is doing more than it needs to:

strObj[str[i]] = strObj[str[i]] += 1;


That appears to be

1. setting the value of strObj[str[i]] to strObj[str[i]] + 1
2. assigning that value to strObj[str[i]] again

It could be simplified to just:

strObj[str[i]] += 1;


or even more simply:

strObj[str[i]]++;


## using forEach with push

Whenever a forEach loop pushes into an array e.g. for output, this can be simplified to array.map()

## prefer const over let

Variables output and strObj can be declared with const instead, since they are only assigned once. This can help avoid accidental re-assignment and other bugs.

# Current code simplified

This code incorporates the advice from suggestions above.

const inputData = [
'aabbc',
'xxxxvvhjh',
'PPPOM'
];
const output = inputData.map(str => {
const strObj = {};
for (let i = 0; i < str.length; i++) {
if (strObj[str[i]] && str[i] === str[i - 1]) {
strObj[str[i]]++;
} else {
strObj[str[i]] = 1;
}
}
return Object.keys(strObj).map((key) => ${key}${strObj[key]}).join('');
});
console.log(output);

# Simplified code with one iteration over each input

This code follows the advice George described, plus suggestions above

const inputData = [
'aabbc',
'xxxxvvhjh',
'PPPOM'
];
const output = inputData.map(str => {
let lastChar, count = 0;
const retVals = [];
for (let i = 0; i < str.length; i++) {
if (!i || lastChar === str[i]) {
count++;
} else if (str[i] !== str[i - 1]) {
retVals.push(str[i - 1], count);
count = 1;
if (i === str.length - 1) {
retVals.push(str[i], 1);
}
}
lastChar = str[i];
}
return retVals.join('');
});
console.log(output);

## Bug

The result for the string "xxxxvvhjh" you defined to be "x4v2h1j1h1" however your function returns "x4v2h1j1" not counting the final h

The reason is that you are using an object to map the letters. When the same letter appears more than once but are not adjacent you only output that character once. Eg the string "hhahhh" your function returns "h3a1" rather than "h2a1h3"

"which counts only adjacent letters in string and gives out count."

Which contradicts the output you have given

[ 'a2b2c1','x4v2h1j1h1','P3O1M1']

Assuming that the output you have given is correct and with the title "run length coder" (assume that is encoder) which reinforces the assumption and the fact that you give the wrong result for "hhahhh" I will presume you are attempting Run length encoding

There is no easy way to correct your code as it is fundamentally flawed. All we can do is start a complete rewrite.

There is no need to use a map as you have done with strObj. The encoding can be done in a single pass

## Run length encoding

Is very simple and fast lossless compression method.

You code should be as functions. With a function to encode a single string, and a second function to encode an array of strings.

### Encode a string

Assuming that the string to encode is always 1 or more characters long

function encodeStr(str) {
var i = 1, char = str[i], count = 1, result = "";
while (i < str.length) {
const next = str[i++];
count = next === char ? count + 1 : (result += char + count, 1);
char = next;
}
return result + char + count;
}


### Encode strings

To encode an array of strings a second function just maps the array using the above function.

const encodeStrs = strs => strs.map(encodeStr);


To use

    function encodeStr(str) {
var i = 1, char = str[i], count = 1, result = "";
while (i < str.length) {
const next = str[i++];
count = next === char ? count + 1 : (result += char + count, 1);
char = next;
}
return result + char + count;
}
const encodeStrs = strs => strs.map(encodeStr);
console.log(encodeStrs(["aabbc", "xxxxvvhjh", "PPPOM"]))

I am not familiar with javascript, but seems like you are iterating over inputs twice, you really just need to iterate over it once. here is how(I might write a pseudo code later) at start:

1. create variable(char) lastCharacter and (int)characterCount

2. create javascript equivalent of c++ struct (I will name this struct "AdjacentCharacters") with variables: character and count

3. create list/vector(or any other resizable array type) of "AdjacentCharacters".

in each iteration:

1. increment characterCount
2. keep track of current character
3. at the end of the iteration set lastCharacter to currentCharacter
4. but before that, compare lastCharacter to currentCharacter, if it matchs or it's first iteration of the loop(since you don't have lastCharacter if it's first)increment characterCount.
5. if it does not match then push in the "AdjacentCharacters" list and set character count to 0

Summary: you are basicaly iterating over string and keeping track of how many same characters you have met, if you meet different character than you reset characterCount to 0, because chain of same characters was broken, and you write down how many same characters you have met and what was that same character to the "AdjacentCharacters" struct and push it to list of "AdjacentCharacters".