Daily Challenge #110 - Love VS. Friendship

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

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

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")  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)
}
}


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 👌

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 :-)

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


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


Ah okay gotcha, thank ya for the tip!

def wordsToMarks(word)
alphabet = ("a".."z").to_a
mark = 0
word.each_char do |char|
mark += alphabet.index(char.downcase) + 1
end
mark
end


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

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... :)

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


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


const wordsToMarks = (word) => word.split("").reduce((acc, curr) => acc + curr.charCodeAt(0) - "a".charCodeAt(0) + 1, 0);


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


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


Nice to see some Prolog :-)

#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;
}


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


import Data.Char (ord)

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


Playground

Here.

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)


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.

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.

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))))


Here is my fun solution using Kotlin:

fun wordsToMarks(word: String) = word.toCharArray()
.map { it + 1 - 'a' }
.sum()


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);
}


My Python Algorithm:

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