Find the highest scoring word from a string according to its position in the alphabet using JavaScript

I am trying to solve CodeWars challenges but I have a problem with this one:

"Given a string of words, you need to find the highest scoring word. Each letter of a word scores points according to its position in the alphabet:

 a = 1, b = 2, c = 3 etc.

You need to return the highest scoring word as a string. If two words score the same, return the word that appears earliest in the original string. All letters will be lowercase and all inputs will be valid."

My code passed 104 cases but got wrong on 1 case. The wrong answer test case is

'what time are we climbing up the volcano' 

According to codewars - Expected: 'volcano', instead got: 'climbing'

Any ideas?

link of the problem - https://www.codewars.com/kata/57eb8fcdf670e99d9b000272/train/javascript

 function high(x){
  let result = '', value =0, counterValue = 0; 

  let splittedArray = x.split(' ');

  splittedArray.map(splitItem => {
    counterValue = 0;

    let splitItemArray = splitItem.split('');

    splitItemArray.map(splitChar => { 
      counterValue += splitChar.charCodeAt();
    })

    if(counterValue>value){
      result = splitItem;
      value = counterValue;
    }
  });
  return result;
}

Upvotes: 1

Views: 9596

Answers (10)

Dmitry Butakov
Dmitry Butakov

Reputation: 1

function high(x){ const ScoreTable = ["0", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k","l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w","x", "y", "z"];

return x.split(" ").map(word => {
    let code = 0
    word.split("").map(letter => code += ScoreTable.indexOf(letter))
    return [word, code]
}).sort((a,b) => b[1] - a[1])[0][0]

}

Upvotes: 0

Josh
Josh

Reputation: 31

Thought I'd post since I solved it, even though the post is pretty old. Anyway this was solved from pure javascript.

//This main function loops thru each sum from the helper function and returns the position of the highest scoring word

function highestScoringWord(sentence) {
let arr = []
let sentenceSplit = sentence.split(' ')
for(i=0; i<sentenceSplit.length; i++) {
arr.push(scoringWord(sentenceSplit[i]))
}
let max = arr[0]
for(x=0; x<arr.length; x++) {
    if(arr[x] > max) {
        max = arr[x]
    }   
}

for(p=0; p<arr.length; p++) {
    if(max === arr[p]) {
        return sentenceSplit[p]
    }
}

}

//Helper function that takes a word, splits it, and sums up the numbers that each letter is worth.

function scoringWord(word) {
 let wordSplit = word.split('')
 let alphabet = 'abcdefghijklmnopqrstuvwxyz'
 let sum = 0
 for(j=0; j<word.length; j++) {
     sum+=alphabet.indexOf(word[j])
 }
 return sum
}

Upvotes: 0

KVR
KVR

Reputation: 1

function high(x){
const str = x.split(' ');
const result1 = [];
const result = str.reduce((_, dig) => {
  let c = 0;
  for (let j = 0; j < dig.length; j++) {
    c = c + (dig.charCodeAt(j) - 96);
  }
  result1.push(c);
}, 0);
return str[result1.indexOf(result1.slice().sort((a, b) => b - a)[0])];
}

Upvotes: 0

I made a mistake by not counting letters position in the alphabet. If I subtract 96 from ASCII value then it will calculate a as 1, b as 2......

So the solution is given below

 function high(x){
  let result = '', value =0, counterValue = 0; 

  let splittedArray = x.split(' ');

  splittedArray.map(splitItem => {
    counterValue = 0;

    let splitItemArray = splitItem.split('');

    splitItemArray.map(splitChar => { 
      counterValue += splitChar.charCodeAt()-96; // if I subtract 96 then it will calculate a as 1, b as 2......
    })

    if(counterValue>value){
      result = splitItem;
      value = counterValue;
    }
  });
  return result;
}

Upvotes: 0

Shahriar Shojib
Shahriar Shojib

Reputation: 927

The charCodeAt() method returns an integer between 0 and 65535 representing the UTF-16 code unit at the given index.

Basically you need to convert it to an uppercase alphabet and subtract the value of the charCodeAt by 64 which will give you the position of the string in the alphabet.

Check this one out:

function high(x) {
    let splittedArray = x.split(' ');
    let splitChar = splittedArray.map(el => el.split(''));
    let charValue = splitChar.map(el => {
        let counter = 0;
        el.map(element => counter += element.toUpperCase().charCodeAt() - 64);
        return counter;
    });
    let largest = 0;
    let largestIndex;
    for (let i = 0; i < charValue.length; i++) {
        if (charValue[i] > largest) {
            largest = charValue[i];
            largestIndex = i;
        }
    }
    return splittedArray[largestIndex];
}

Upvotes: 0

Asher G.
Asher G.

Reputation: 5001

Idea:

  • Store the score values in a table for easy lookup
  • split sentences into words by non-alphabetic characters
  • get each word's score by first calculating the individual characters' scores and then summing them together (using reduce).
  • Don't forget to sort by original position if scores are the same, do it by keeping track of the original position.

Demo: https://jsfiddle.net/9xkfqh1m/

const ScoreTable = {
  "a": 1,
  "b": 2,
  "c": 3,
  "d": 4,
  "e": 5,
  "f": 6,
  "g": 7,
  "h": 8,
  "i": 9,
  "j": 10,
  "k": 11,
  "l": 12,
  "m": 13,
  "n": 14,
  "o": 15,
  "p": 16,
  "q": 17,
  "r": 18,
  "s": 19,
  "t": 20,
  "u": 21,
  "v": 22,
  "w": 23,
  "x": 24,
  "y": 25,
  "z": 26
};

// non-ascii letters = 0
function CharScore(char) {
  const lowerCaseChar = char.toLowerCase();
  return lowerCaseChar in ScoreTable ? ScoreTable[lowerCaseChar] : 0;
}

function WordScore(word) {
  return word.split("").reduce((score, char) => {
    return score + CharScore(char);
  }, 0);
}

function SplitOnNonAlphabeticCharacters(str) {
  return str.split(/[^a-zA-Z]+/gm);
}

function high(str){
  const scoreObjects = SplitOnNonAlphabeticCharacters(str)        // split into words
  .map((word, index) => {                                         // map our words into an object with its score and original position
    return {
      text: word,
      score: WordScore(word),
      originalPosition: index
    };
  }).sort((word1, word2) => {                                        // sort
    return word2.score - word1.score                                 // highest score first
                ||  word1.originalPosition - word2.originalPosition; // if score same, earliest original position in string
  });

  return scoreObjects.length > 0 ? scoreObjects[0].text : null;   // return word by the highest score (or earliest original position), or null
}

Upvotes: 0

Jason Lydon
Jason Lydon

Reputation: 7180

I'm confused by your counterValue += splitChar.charCodeAt(); line. I don't understand how splitChar.charCodeAt() translates into 1-26 aka a letters position in the alphabet. "Each letter of a word scores points according to its position in the alphabet"

I was able to able to make your code work by doing two things:

  1. Add a map to store the value of each letter in the alphabet. I'm sure this can be done many ways but this was my approach:
let letterValues = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10, k: 11, l: 12, m: 13, n: 14, o: 15, p: 16, q: 17, r: 18, s: 19, t: 20, u: 21, v: 22, w: 23, x: 24, y: 25, z: 26 };
  1. And then use this in counterValue += splitChar.charCodeAt(); as counterValue += letterValues[letter];

Upvotes: 0

Adarsh Hegde
Adarsh Hegde

Reputation: 631

the solution is to use an array of the alphabet and indexing the character position in it,

let al = `abcdefghijklmnopqrstuvwxyz`.split('')


   function high(x){
     let words = x.split(" ");
     let out = words.map(word => {
     let score = 0;
     let letters = word.split("");
     letters.map(c => {
      score += al.indexOf(c);
     })
     return [word, score];
    });

    out = out.sort((a,b) => { 
      if(a[1] > b[1]) return -1;
      else if(a[1] < b[1]) return 1; 
      else return 0;  });

return out[0][0];
}

Upvotes: 1

Vazgen
Vazgen

Reputation: 59

function high(x) {
  const words = x.split(' ');
  const alphabetMap = {};
  for (let i='a'.charCodeAt(), j = 1; i <= 'z'.charCodeAt(); i++, j++) {
    alphabetMap[i] = j;
  }
  let highestScoringWord = { word: '', score: 0 };
  words.forEach(w => {
    const chars = w.split('');
    const sumOfChars = chars.reduce((count, char) => count + alphabetMap[char.charCodeAt()], 0);
    if (sumOfChars > highestScoringWord.score) {
      highestScoringWord = { word: w, score: sumOfChars };
    }
  });

  return highestScoringWord.word;
}

console.log(high('what time are we climbing up the volcano')) // volcano ;)

Upvotes: 1

Code Maniac
Code Maniac

Reputation: 37755

You can use reduce and object to keep track of highest count and respective word

function high(x){
  let mapper = [...`abcdefghijklmnopqurstuvwxyz`].reduce((op,inp,index)=>{
    op[inp] = index+1
    return op
  },{})
  return x.split(' ').reduce((op,inp)=>{
    let currentCount = 0;
    [...inp].forEach(v=>{
      currentCount += mapper[v]
    })
    if(currentCount > op.maxCount){
      op.maxCount = currentCount
      op.word = inp
    }
    return op
  }, {maxCount:0, word:''}).word
}

console.log(high('what time are we climbing up the volcano'), 'volcano'))

Upvotes: 1

Related Questions