loading...

Daily Challenge #110 - Love VS. Friendship

thepracticaldev profile image dev.to staff ・1 min read

If a = 1, b = 2, c = 3 ... z = 26

Then l + o + v + e = 54

and f + r + i + e + n + d + s + h + i + p = 108

So friendship is twice as strong as love :-)

Write a function to find the "strength" of each of these values.

Test cases:
wordsToMarks(attitude)
wordsToMarks(friends)
wordsToMarks(family)
wordsToMarks(selflessness)
wordsToMarks(knowledge)

Good luck!


This challenge comes from J or nor J on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Discussion

pic
Editor guide
Collapse
ynndvn profile image
La blatte

A bit like @nicolas Hervé, using the reduce method:

f=a=>[...a].reduce((t,l)=>t+l.charCodeAt``-96,0)
  • [...a] transforms the string to an array with every character (["l", "o", "v", "e"])
  • The reduce methods will loop through every character, get its charCode, subtract 96 to it (the charCode associated with "a" + 1), and sum everything
Collapse
kespri profile image
kespri

Thanks to @ynndvn for showing me reduce method, it exist too in Swift language and it's awesome !!!

This is my solution in Swift :

func wordsToMarks(word: String) -> Int {
    return word.compactMap { $0 }.reduce(0) {
       $0 + Int($1.asciiValue ?? 0) - 96
    }
}

wordsToMarks(word:"attitude")
wordsToMarks(word:"friends")
wordsToMarks(word:"family")
wordsToMarks(word:"selflessness")
wordsToMarks(word:"knowledge")
Collapse
citizen428 profile image
Michael Kohl

Why the compactMap, you are mapping each letter to itself, works fine without that too.

Also when I tried your solution with the input string "love🐈" it returned -42 since the way you handle the Optional results in non-ASCII characters having a value of -96. The following version simply ignores such characters instead:

func wordsToMarks(word: String) -> Int {
    return word.reduce(0) {
       $0 + Int($1.asciiValue.map { $0 - 96 } ?? 0)
    }
}
Collapse
kespri profile image
kespri

Oh nice! That's better !

I forgot String are a table of Character so you right compactMap is useless.
I have totally ignored emojis because the challenger treat only letter a to z

So thank you for your feedback, your optimisation is truly better 👌

Thread Thread
citizen428 profile image
Michael Kohl

I have totally ignored emojis because the challenger treat only letter a to z

True, but you did add some code to deal with the case were a character may not have an asciiValue, I just offered an alternative to that :-)

Collapse
dak425 profile image
Donald Feury

I started picking up ruby for rails, so here is my shining gem.

Try it out here

word.rb

class Word
  @@alphabet = ('a'..'z').to_a

  def self.to_mark(word)
    word.chars.sum { |char| @@alphabet.index(char.downcase) + 1 }
  end
end

word_test.rb

require 'minitest/autorun'
require_relative 'word'

class TestWord < Minitest::Test
  def setup
    @cases = [
      { input: 'love', expected: 54 },
      { input: 'friendship', expected: 108 },
      { input: 'attitude', expected: 100 },
      { input: 'friends', expected: 75 },
      { input: 'family', expected: 66 },
      { input: 'selflessness', expected: 154 },
      { input: 'knowledge', expected: 96 }
    ]
  end

  def test_to_mark
    @cases.each { |test| assert_equal test[:expected], Word.to_mark(test[:input]) }
  end
end
Collapse
citizen428 profile image
Michael Kohl

Just a friendly tip: alphabet really shouldn't be a class variable @@, they are very seldom what you want in Ruby:

railstips.org/blog/archives/2006/1...

In this specific case a constant would be the most common:

ALPHABET = ('a'..'z').to_a
Collapse
dak425 profile image
Donald Feury

Ah okay gotcha, thank ya for the tip!

Collapse
yechielk profile image
Yechiel Kalmenson
def wordsToMarks(word)
  alphabet = ("a".."z").to_a
  mark = 0
  word.each_char do |char|
    mark += alphabet.index(char.downcase) + 1
  end
  mark
end
Collapse
scrabill profile image
Shannon Crabill

("a".."z").to_a is nifty!

Collapse
yechielk profile image
Yechiel Kalmenson

Well, my first attempt had me iterating calling .index on ("a".."z") only to find out that you can't call index on a range, so in trying to work around that I realized I could just convert the range into an array... :)

Collapse
citizen428 profile image
Michael Kohl

Ruby:

def words_to_marks(word)
  word.chars.sum { |c| c.ord - 96 }
end

For fun a second version that builds a lookup table with an endless range (introduced in Ruby 2.6):

ALPHABET = ('a'..'z').zip(1..).to_h

def words_to_marks(word)
  word.chars.sum { |c| ALPHABET.fetch(c, 0) }
end
Collapse
peter279k profile image
peter279k

Here is the simple solution with Python:

def words_to_marks(s):
    alphabet = {
        '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,
    }

    sum_length = 0
    for char in s:
        sum_length += alphabet[char]

    return sum_length
Collapse
hnicolas profile image
Nicolas Hervé
const wordsToMarks = (word) => word.split("").reduce((acc, curr) => acc + curr.charCodeAt(0) - "a".charCodeAt(0) + 1, 0);
Collapse
khenhey profile image
Karin

A little solution for Ruby :) this was fun!

def wordsToMarks(word)
  strength = 0
  alphabet = ('a'..'z').to_a
  word.split('').each do |i|
    strength = strength + (alphabet.index(i) + 1)
  end
  return strength
end
Collapse
jbristow profile image
Jon Bristow
words_to_marks("",0).
words_to_marks(S,Mark) :-
    string_codes(S,SC),
    length(SC, SCL),
    sum_list(SC,SCS),
    Mark is SCS - SCL * 96.
?- words_to_marks(“love”, V).
% V = 54

?- words_to_marks(“declarative”, V).
% V = 100
Collapse
citizen428 profile image
Michael Kohl

Nice to see some Prolog :-)

Collapse
edreeseg profile image
Ed Reeseg
#include <stdio.h>

int main(int argc, char *argv[]) 
{
    if (argc != 2)
    {
        printf("Usage: ./strength string\n");
        return 1;
    }
    int sum = 0;
    char *c = argv[1];
    while(*c != '\0')
    {
        if (*c < 'a' || *c > 'z')
        {
            printf("Invalid argument\n");
            return 1;
        }
        sum += *c - 'a' + 1;
        c++;
    }
    printf("%i\n", sum);
    return 0;
}
Collapse
lloricode profile image
Lloric Mayuga Garcia

In PHP


$alpha = function (string $string) {
    $al = array_flip(range('a', 'z'));

    $points = 0;
    foreach (str_split($string) as $s) {
        $points += $al[$s] + 1;
    }
    return $points;
};

echo $alpha('friendship');

EDIT:

array_map(function (string $string) {
    $al = array_flip(range('a', 'z'));

    $points = 0;
    foreach (str_split($string) as $s) {
        $points += $al[$s] + 1;
    }
    echo "$string: $points<br>";
}, ['attitude', 'friends', 'family', 'selflessness', 'knowledge']);

attitude: 100
friends: 75
family: 66
selflessness: 154
knowledge: 96
Collapse
aminnairi profile image
Amin

Haskell

import Data.Char (ord)

wordsToMarks :: String -> Int
wordsToMarks = sum . map ((flip (-) 96) . ord)

Playground

Here.

Collapse
citizen428 profile image
Michael Kohl

The prelude defines subtract, so you don't need flip here:

wordsToMarks = sum . map (subtract 96 . ord)

Another relatively common workaround is to use a right-section that adds a negative number, i.e.

wordsToMarks = sum . map ((+(-96)) . ord)
Collapse
aminnairi profile image
Amin

Thanks for your insight. I didn't know about subtract and when the second solution is kind of obscure to me it seems very cool and short.

Thread Thread
citizen428 profile image
Michael Kohl

Since - is both subtraction and unary minus one can’t use a right-section like (-1), but considering that subtracting is the same as adding a negative number the slightly obscure form makes sense. I’d still use subtract though.

Collapse
tblanshard profile image
Tallie

My solution to the challenge using map and reduce in Python. If anyone has any suggestions on how to improve it, I would love to hear! :)

from functools import reduce 
def wordsToMarks(word):
    print(reduce((lambda x,y: x+y), map((lambda x: ord(x) - 96), list(word))))
Collapse
konvio profile image
Vitaliy Kononenko

Here is my fun solution using Kotlin:

fun wordsToMarks(word: String) = word.toCharArray()
        .map { it + 1 - 'a' }
        .sum()
Collapse
peledzohar profile image
Zohar Peled

c#, one liner using linq, ignoring non-letters chars:

int WordsToMarks(string word)
{
    return word.Where(c => char.IsLetter(c)).Sum(c => char.ToUpperInvariant(c) - 64);
}
Collapse
herobank110 profile image
David Kanekanian

My Python Algorithm:

words_to_marks = lambda words: sum(
    map(ord, words.lower()),
    -96 * len(words))