19
\$\begingroup\$

The goal of this challenge is to compute one kind of numerology digit from strings containing characters and numbers.

  • The input may be through any convenient method (standard input, arguments, separated file).
  • The input may contain any printable ASCII chars, but only alphanumerical (A-Z, a-z, and 0-9) have to be considered.
  • The output must be a digit between 1 and 9 or a star * if no letter and no digit where found... (or even 0 if input contain any number of 0 and nothing else but this doesn't matter).
  • Letter values are mapped in this way:

    1  2  3  4  5  6  7  8  9
    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
    
  • The numerology digit is computed by adding each value of string, then repeat until there is only one digit. Sample for 13579, Hello, world!, 00 0 00, !@#$%^&*();, and 3.141592:

    13579 => 1 + 3 + 5 + 7 + 9 = 25 => 2 + 5 = 7
    Hello, world! => 8 + 5 + 3 + 3 + 6 + 5 + 6 + 9 + 3 + 4 = 52 => 5 + 2 = 7
    00 0 00 => 0 + 0 + 0 + 0 + 0 = 0
    !@#$%^&*(); => *
      => *
    3.141592 => 3 + 1 + 4 + 1 + 5 + 9 + 2 = 25 => 2 + 5 = 7
    3.1415926535897932384 => 
     3 + 1 + 4 + 1 + 5 + 9 + 2 + 6 + 5 + 3 + 5 + 8 + 9 + 7 + 9 + 3 + 2 + 3 + 8 + 4
     = 97 => 9 + 7 = 16 => 1 + 6 = 7
    

    (This is great, most of this samples give 7! But it's just sample ;)

    Some more tests:

    Bob  => 2 + 6 + 2 = 10 => 1 + 0 = 1
    Charlie => 3 + 8 + 1 + 9 + 3 + 9 + 5 = 38 => 3 + 8 = 11 => 1 + 1 = 2
    Anna => 1 + 5 + 5 + 1 = 12 => 1 + 2 = 3
    Fana => 6 + 1 + 5 + 1 = 13 => 1 + 3 = 4
    Gregory => 7 + 9 + 5 + 7 + 6 + 9 + 7 = 50 => 5 + 0 = 5
    Denis => 4 + 5 + 5 + 9 + 1 = 24 => 2 + 4 = 6
    Erik => 5 + 9 + 9 + 2 = 25 => 2 + 5 = 7
    Helen => 8 + 5 + 3 + 5 + 5 = 26 => 2 + 6 = 8
    Izis => 9 + 8 + 9 + 1 = 27 => 2 + 7 = 9
    

This is a , so the shortest answer in bytes wins.

Shortest by language

<style>body { text-align: left !important} #answer-list { padding: 10px; width: 290px; float: left; } #language-list { padding: 10px; width: 290px; float: left; } table thead { font-weight: bold; } table td { padding: 5px; }</style><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="language-list"> <h2>Shortest Solution by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr> </thead> <tbody id="languages"> </tbody> </table> </div> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr> </thead> <tbody id="answers"> </tbody> </table> </div> <table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table><script>var QUESTION_ID = document.referrer.split("/")[4]; var ANSWER_FILTER = "!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe"; var COMMENT_FILTER = "!)Q2B_A2kjfAiU78X(md6BoYk"; var OVERRIDE_USER = 0; var answers = [], answers_hash, answer_ids, answer_page = 1, more_answers = true, comment_page; function answersUrl(index) { return "//api.stackexchange.com/2.2/questions/" + QUESTION_ID + "/answers?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + ANSWER_FILTER; } function commentUrl(index, answers) { return "//api.stackexchange.com/2.2/answers/" + answers.join(';') + "/comments?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + COMMENT_FILTER; } function getAnswers() { jQuery.ajax({ url: answersUrl(answer_page++), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { answers.push.apply(answers, data.items); answers_hash = []; answer_ids = []; data.items.forEach(function(a) { a.comments = []; var id = +a.share_link.match(/\d+/); answer_ids.push(id); answers_hash[id] = a; }); if (!data.has_more) more_answers = false; comment_page = 1; getComments(); } }); } function getComments() { jQuery.ajax({ url: commentUrl(comment_page++, answer_ids), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { data.items.forEach(function(c) { if (c.owner.user_id === OVERRIDE_USER) answers_hash[c.post_id].comments.push(c); }); if (data.has_more) getComments(); else if (more_answers) getAnswers(); else process(); } }); } getAnswers(); var SCORE_REG = /<h\d>\s*([^\n,<]*(?:<(?:[^\n>]*>[^\n<]*<\/[^\n>]*>)[^\n,<]*)*),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/; var OVERRIDE_REG = /^Override\s*header:\s*/i; function getAuthorName(a) { return a.owner.display_name; } function process() { var valid = []; answers.forEach(function(a) { var body = a.body; a.comments.forEach(function(c) { if(OVERRIDE_REG.test(c.body)) body = '<h1>' + c.body.replace(OVERRIDE_REG, '') + '</h1>'; }); var match = body.match(SCORE_REG); if (match) valid.push({ user: getAuthorName(a), size: +match[2], language: match[1], link: a.share_link, }); else console.log(body); }); valid.sort(function (a, b) { var aB = a.size, bB = b.size; return aB - bB }); var languages = {}; var place = 1; var lastSize = null; var lastPlace = 1; valid.forEach(function (a) { if (a.size != lastSize) lastPlace = place; lastSize = a.size; ++place; var answer = jQuery("#answer-template").html(); answer = answer.replace("{{PLACE}}", lastPlace + ".") .replace("{{NAME}}", a.user) .replace("{{LANGUAGE}}", a.language) .replace("{{SIZE}}", a.size) .replace("{{LINK}}", a.link); answer = jQuery(answer); jQuery("#answers").append(answer); var lang = a.language; lang = jQuery('<a>'+lang+'</a>').text(); languages[lang] = languages[lang] || {lang: a.language, lang_raw: lang.toLowerCase(42), user: a.user, size: a.size, link: a.link}; }); var langs = []; for (var lang in languages) if (languages.hasOwnProperty(lang)) langs.push(languages[lang]); langs.sort(function (a, b) { if (a.lang_raw > b.lang_raw) return 1; if (a.lang_raw < b.lang_raw) return -1; return 0; }); for (var i = 0; i < langs.length; ++i) { var language = jQuery("#language-template").html(); var lang = langs[i]; language = language.replace("{{LANGUAGE}}", lang.lang) .replace("{{NAME}}", lang.user) .replace("{{SIZE}}", lang.size) .replace("{{LINK}}", lang.link); language = jQuery(language); jQuery("#languages").append(language); } }</script>

\$\endgroup\$
  • \$\begingroup\$ 0 will diseapear when added to anything else \$\endgroup\$ – F. Hauri Dec 24 '15 at 21:37
  • \$\begingroup\$ 0 is not relevant! Relevant output is between 1 and 9! \$\endgroup\$ – F. Hauri Dec 24 '15 at 21:43
  • \$\begingroup\$ Could you add some examples featuring no alphanumeric characters? \$\endgroup\$ – ETHproductions Dec 24 '15 at 22:20
  • 1
    \$\begingroup\$ I mean something like !@#$%^&*(); something which should return *. \$\endgroup\$ – ETHproductions Dec 24 '15 at 22:32
  • \$\begingroup\$ It has been very hard to resist citing this in some of the technical-analysis discussions on the Money area... ;-p \$\endgroup\$ – keshlam Dec 26 '15 at 15:12

15 Answers 15

6
\$\begingroup\$

Matlab, 121 bytes

s=[input('','s'),'*'];while nnz(s)>1;s=num2str(sum(mod([s(48<s&s<58)-4,s(96<s&s<123)+2,s(64<s&s<91)-2],9)+1));end;disp(s)

Matlab is just not made for strings =(

\$\endgroup\$
  • 3
    \$\begingroup\$ Hey, at least it isn't as verbose as Mma :P \$\endgroup\$ – LegionMammal978 Dec 24 '15 at 23:48
  • \$\begingroup\$ my +1, I couldn't do better. btw, is it necessary to put disp statement? \$\endgroup\$ – brainkz Dec 25 '15 at 14:22
  • \$\begingroup\$ @brainkz Maybe, but I usually incude it to be on the safe side \$\endgroup\$ – flawr Dec 25 '15 at 15:14
3
\$\begingroup\$

Mathematica, 174 168 163 bytes

Catch[#-9Floor[Max[#-1,1]/9]&@If[(a=Tr[Characters@#/.{(b=a_String)?DigitQ:>FromDigits@a,b?LetterQ:>LetterNumber@a~Mod~9,b->0}])<1&&#~StringFreeQ~"0",Throw@"*",a]]&

Does the first step, then computes the digital root.

\$\endgroup\$
3
\$\begingroup\$

Ruby, 97 74 characters

n=->s{(t=eval s.tr('a-z9A-Z','1-9'*6).scan(/\d/)*?+)&&t>9?n[t.to_s]:t||?*}

Sample run:

2.1.5 :001 > n=->s{(t=eval s.tr('a-z9A-Z','1-9'*6).scan(/\d/)*?+)&&t>9?n[t.to_s]:t||?*}
 => #<Proc:0x00000001b4b3f0@(irb):4 (lambda)> 

2.1.5 :002 > puts ['13579', 'Hello, world!', '00 0 00', '!@#$%^&*();', ' ', '3.141592', '3.1415926535897932384', 'Bob', 'Charlie', 'Anna', 'Izis'].map{|s|'%s : %s'%[n[s],s]}
7 : 13579
7 : Hello, world!
0 : 00 0 00
* : !@#$%^&*();
* :  
7 : 3.141592
7 : 3.1415926535897932384
1 : Bob
2 : Charlie
3 : Anna
9 : Izis
\$\endgroup\$
3
\$\begingroup\$

Perl, 91 89 76 74 bytes

73 + 1 for -p switch

s/[a-z]/(ord($&)%32-1)%9+1/eig;$t="*",s/\d/$t+=$&/eg,$_=$t until/^[*\d]$/

Tests

for test in '13579' 'Hello, world!' '00 0 00' '!@#$%^&*();' ' ' \
    '3.141592' '3.1415926535897932384' \
    Bob Charlie Anna Fana Gregory Denis Erik Helen Izis ;do  
    perl -pe '
      s/[a-z]/(ord($&)%32-1)%9+1/eig;$t="*",s/\d/$t+=$&/eg,$_=$t until/^[*\d]$/
      ' < <(echo -n "$test")
    echo "  $test"
done
7  13579
7  Hello, world!
0  00 0 00
*  !@#$%^&*();
*   
7  3.141592
7  3.1415926535897932384
1  Bob
2  Charlie
3  Anna
4  Fana
5  Gregory
6  Denis
7  Erik
8  Helen
9  Izis

Thanks @manatwork for helping me to save 2 14 16 15 17 chars!!

... I've thinked about: N % 32 + Y may replace ( N & 31 ) + Y!

\$\endgroup\$
  • 1
    \$\begingroup\$ If you capture the whole matched substring, $1 is equal with $&. So better remove the capturing and change the variable name. \$\endgroup\$ – manatwork Dec 25 '15 at 11:26
  • 1
    \$\begingroup\$ while's block could be $t="*";s/\d/$t+=$&/eg;$_=$t. \$\endgroup\$ – manatwork Dec 25 '15 at 12:10
  • 1
    \$\begingroup\$ Sorry, but its actually 1 character longer, as you should include the -p switch in the count. \$\endgroup\$ – manatwork Dec 25 '15 at 12:42
  • 1
    \$\begingroup\$ Sorry again (this time a bigger sorry), but it fails on single non-word character input, for example “!” results the same ”!”. (Seems to work because with here-string the input is always passed with a trailing newline, so in your test cases never passes single character input. Use echo -n "$test" | perl -pe '…' to see what I mean.) You can solve it by changing the while condition to !/^[\d*]$/. While there, to reduce the score loss, you may replace the entire while with this: $t="*",s/\d/$t+=$&/eg,$_=$t until/^[\d*]$/. \$\endgroup\$ – manatwork Dec 25 '15 at 13:08
  • 1
    \$\begingroup\$ There is one more improvement you can do. As & has lower precedence than +, while % has higher, change &31%32 and you can remove the parentheses around that subexpression. \$\endgroup\$ – manatwork Dec 25 '15 at 13:44
3
\$\begingroup\$

ES6, 98 bytes

s=>(m=s.match(/[1-9a-z]/gi))?(t=8,m.map(c=>t+=c>'@'?c.charCodeAt()&31:+c),t%9+1):/0/.test(s)?0:'*'

Ungolfed:

function(s) {
    var m = s.match(/[1-9a-z]/gi);
    if (m) {
        var t = 0;
        for (var i = 0; i < m.length; i++) {
            if (m[i] > '@')
                t += m[i].charCodeAt(0) & 31;
            else
                t += parseInt(m[i]);
        }
        return t % 9 || 9;
    }
    return /0/.test(s) ? 0 : "*";
}

94-byte version that only works on short strings:

s=>(m=s.match(/[1-9a-z]/gi))?m.map(c=>c>'@'?c.charCodeAt()&31:c).join``%9||9:/0/.test(s)?0:'*'

Using match, map and join turned out to be shorter than using replace twice:

s=>(m=s.replace(/[^1-9a-z]/gi,''))?m.replace(/[a-z]/gi,c=>c.charCodeAt()&31)%9||9:/0/.test(s)?0:'*'

Test it here: https://jsbin.com/zizosayisi/edit?js,console

\$\endgroup\$
  • \$\begingroup\$ This doesn't work for the input 00 0 00 \$\endgroup\$ – rink.attendant.6 Dec 25 '15 at 20:28
  • \$\begingroup\$ @rink.attendant.6 Ah, the REPL I was using just prints ["0"] as 0 so I couldn't tell the difference. \$\endgroup\$ – Neil Dec 25 '15 at 21:02
  • \$\begingroup\$ Much, much better than @rink.attendant.6 version! Clever golf hacks used. Could you explain > '@', & 31 and || 9? \$\endgroup\$ – Pavlo Dec 26 '15 at 12:35
  • 1
    \$\begingroup\$ @Pavlo > '@' just distinguishes between letters and numbers. & 31 is a useful way of ignoring the difference between upper and lower case character codes, as it also conveniently directly maps character codes into 1..26 values. || 9 is used because % 9 returns 0 for multiples of 9 but repeatedly adding digits returns 9 for non-zero multiples of 9. In the golfed code I use (t + 8) % 9 + 1 instead which comes to the same thing. \$\endgroup\$ – Neil Dec 26 '15 at 19:30
  • \$\begingroup\$ The language is commonly named "JavaScript (ES6)" \$\endgroup\$ – edc65 Dec 27 '15 at 12:14
2
\$\begingroup\$

Gema, 161 characters

*=@n{*}
n:\A=@set{t;};<L1>=@set{t;@add{$t;@add{@mod{@sub{@mod{@char-int{$0};32};1};9};1}}};<D1>=@set{t;@add{$t;$0}};?=;\Z=@cmps{$t;;;\*;@cmpn{$t;9;$t;$t;@n{$t}}}

(Written only to try whether recursive domain calls work.)

Sample run:

bash-4.3$ for input in '13579' 'Hello, world!' '00 0 00' '!@#$%^&*();' ' ' '3.141592' '3.1415926535897932384'; do
>     echo -n "'$input' : "
>     gema '*=@n{*};n:\A=@set{t;};<L1>=@set{t;@add{$t;@add{@mod{@sub{@mod{@char-int{$0};32};1};9};1}}};<D1>=@set{t;@add{$t;$0}};?=;\Z=@cmps{$t;;;\*;@cmpn{$t;9;$t;$t;@n{$t}}}' <<< "$input"
>     echo
> done
'13579' : 7
'Hello, world!' : 7
'00 0 00' : 0
'!@#$%^&*();' : *
' ' : *
'3.141592' : 7
'3.1415926535897932384' : 7
\$\endgroup\$
1
\$\begingroup\$

JavaScript (ES6), 162 159 157 bytes

f=_=>{for(_=_.replace(/\W+/g,''),g=$=>''+[...$.toUpperCase()].reduce((p,a)=>isNaN(a)?p+(a.charCodeAt()-64)%9:+a+p,0);1<(l=_.length);_=g(_));return!l?'*':g(_)}

Still trying to look for a way to use implicit return in the outer function.

Ungolfed + unminified

f = str => {
  str = str.replace(/\W+/g, '');
  recursiveFunc = $ => String([...$.toUpperCase()].reduce(
    (prev, val) => isNaN(val) ? prev + (val.charCodeAt() - 64) % 9 : Number(val) + prev,
    0
  ));
  while (1 < (len = str.length)) {
    str = recursiveFunc(str);
  }
  return len === 0 ? '*' : recursiveFunc(str)
}
  1. Strips out all non-alphanumeric characters
  2. Calls a function recursively to reduce the characters to their respective values while the string is longer than 1 character.
    1. Converts the string to uppercase to easily work with ASCII codes
    2. Convert to array using spread operator and run an accumulator
    3. Use the global isNaN function (which casts its input) to check if it's not a number
      • If it isn't, convert to ASCII code and mod 9 to get its respective value
      • If it is a number, cast it
  3. If the length is 0, no alphanumeric characters were present (return an asterisk), otherwise return the output of the recursive function.
\$\endgroup\$
  • \$\begingroup\$ This break rule 00 0 00, output * instead of 0! \$\endgroup\$ – F. Hauri Dec 25 '15 at 10:36
  • \$\begingroup\$ @F.Hauri Fixed. \$\endgroup\$ – rink.attendant.6 Dec 25 '15 at 11:05
  • \$\begingroup\$ You can avoid explicit return by using comman operator: _=>{expr;return expr} => _=>(expr,expr) \$\endgroup\$ – Pavlo Dec 25 '15 at 12:50
  • \$\begingroup\$ @Pavlo How do I apply that in my case? \$\endgroup\$ – rink.attendant.6 Dec 25 '15 at 20:29
  • \$\begingroup\$ Doesn't work for me, throws SyntaxError: jsbin.com/havotusoqa/1/edit?js,console \$\endgroup\$ – Pavlo Dec 28 '15 at 11:48
1
\$\begingroup\$

Haskell, 126 bytes

l x=[n|(c,n)<-zip(['0'..'9']++['a'..'z']++'\0':['A'..'Z'])$0:cycle[1..9],c==x]
g[]="*"
g[x]=show x
g x=f$show$sum x
f=g.(l=<<)

Usage: f "Hello, world!" -> "7".

l is a lookup table for characters to list of integers (singleton list, if char is found, empty list otherwise). f lookups every char of it's argument and flattens the returned list of lists into a simple list of integers and calls g to check the end condition (empty list (-> *) or single integer) or to call f with the sum of the list for another round.

\$\endgroup\$
1
\$\begingroup\$

MATL, 64 bytes

jk42ht`YUt'[a-z]'XXY}3+w'[1-9]'XXY}6+h,9X\st9>]w2Y24Y2h!=~?x'*']

This uses current version (4.0.0) of the language.

I have a feeling that it could be made shorter...

Test cases

>> matl
 > jk42ht`YUt'[a-z]'XXY}3+w'[1-9]'XXY}6+h,9X\st9>]w2Y24Y2h!=~?x'*']
 > 
> 13579
7

>> matl
 > jk42ht`YUt'[a-z]'XXY}3+w'[1-9]'XXY}6+h,9X\st9>]w2Y24Y2h!=~?x'*']
 > 
> Hello, world!
7

>> matl
 > jk42ht`YUt'[a-z]'XXY}3+w'[1-9]'XXY}6+h,9X\st9>]w2Y24Y2h!=~?x'*']
 > 
> 00 0 00
0

>> matl 
 > jk42ht`YUt'[a-z]'XXY}3+w'[1-9]'XXY}6+h,9X\st9>]w2Y24Y2h!=~?x'*']
 > 
> !@#$%^&*();
*

>> matl
 > jk42ht`YUt'[a-z]'XXY}3+w'[1-9]'XXY}6+h,9X\st9>]w2Y24Y2h!=~?x'*']
 > 
> 3.141592
7

>> matl
 > jk42ht`YUt'[a-z]'XXY}3+w'[1-9]'XXY}6+h,9X\st9>]w2Y24Y2h!=~?x'*']
 > 
> 3.1415926535897932384
7

>> matl
 > jk42ht`YUt'[a-z]'XXY}3+w'[1-9]'XXY}6+h,9X\st9>]w2Y24Y2h!=~?x'*']
 > 
> Bob
1

>> matl
 > jk42ht`YUt'[a-z]'XXY}3+w'[1-9]'XXY}6+h,9X\st9>]w2Y24Y2h!=~?x'*']
 > 
> Charlie
2

>> matl
 > jk42ht`YUt'[a-z]'XXY}3+w'[1-9]'XXY}6+h,9X\st9>]w2Y24Y2h!=~?x'*']
 > 
> Anna
3

>> matl
 > jk42ht`YUt'[a-z]'XXY}3+w'[1-9]'XXY}6+h,9X\st9>]w2Y24Y2h!=~?x'*']
 > 
> Izis
9
\$\endgroup\$
  • \$\begingroup\$ Unfortunely, I could'nt test your sample. Please post whole test cases, including recently added tests (sorry). \$\endgroup\$ – F. Hauri Dec 25 '15 at 17:36
  • \$\begingroup\$ @F.Hauri Yes, you'd need Matlab to test them. Sorry about that, no online compiler yet. I've added the test cases \$\endgroup\$ – Luis Mendo Dec 25 '15 at 17:48
1
\$\begingroup\$

Seriously, 50 bytes

,$ù;ú1╤▀+;#pX╗@-@-;Y`'*.`╬X1WX`╜í;s9*@%u`MΣ$;lDWX

Hex Dump:

2c24973ba331d1df2b3b237058bb402d402d3b5960272a2e7f
60ce5831575860bda13b73392a402575604de4243b6c445758

Try It Online

Explained:

,$ù                                               Read input, make it a string, lowercase
    ú                                             Push lowercase alphabet
     1╤▀+                                         Prepend base 10 digits.
         ;#pX╗                                    Remove "0" from a copy and stash in reg0
   ;          @-                                  Remove alphanumerics from input copy
                @-                                Remove nonalphanumerics from input
                  ;Y                              Push 1 if string is empty, else 0
                    `'*.`╬                        If top is truthy, output * and halt
                          X                       Discard boolean
                           1                      Push 1 to enter loop
                            WX                 WX Loop while top of stack is truthy
                              `         `M        Map this function over the string
                               ╜                  Push alphanumeric string from reg0
                                í                 Push index of this char in it
                                 ;s9*             Push 9 if found, else -9
                                     @%u          Take index mod previous: this yields the
                                                  correct conversion from the numerology
                                          Σ       Sum the resulting digits.
                                           $      Convert the sum to a string.
                                            ;lD   push 1 less than its length
\$\endgroup\$
  • \$\begingroup\$ @F.Hauri Click "Try It Online". enter your test cases in the input box. \$\endgroup\$ – quintopia Dec 27 '15 at 8:33
1
\$\begingroup\$

Pyth, 39 bytes

IK@J+jkUTGrz0WtK=K`smh%xtJd9-K\0)K).?\*

Try It Online

I'm just doing this because I can't sleep. Maybe I'll explain it tomorrow...

\$\endgroup\$
1
\$\begingroup\$

Pure bash, 199 194 bytes

eval a+={a..z};r="$1";while [ "${r:1}" ];do o=;for ((i=0;i<${#r};i++));do
l=${r:i:1};case $l in [a-zA-Z])d=${a%${l,}*};((o+=$((${#d}%9+1))));;[0-9])
((o+=l));;esac;done;r="$o";done;echo "${o:-*}"

(second line break is only for avoiding scrollbar)

Test rule:

numerology() {
    eval a+={a..z};
    r="$1";
    while [ "${r:1}" ]; do
        o=;
        for ((i=0; i<${#r}; i++))
        do
            l=${r:i:1};
            case $l in 
                [a-zA-Z])
                    d=${a%${l,}*};
                    ((o+=$((${#d}%9+1))))
                ;;
                [0-9])
                    ((o+=l))
                ;;
            esac;
        done;
        r="$o";
    done;
    echo "${o:-*}"
}

for test in '13579' 'Hello, world!' '00 0 00' '!@#$%^&*();' ' ' \
            '3.141592' '3.1415926535897932384'\
            Bob Charlie Anna Fana Gregory Denis Erik Helen Izis ;do
    echo "$(numerology "$test")" $test
done
7 13579
7 Hello, world!
0 00 0 00
* !@#$%^&*();
*
7 3.141592
7 3.1415926535897932384
1 Bob
2 Charlie
3 Anna
4 Fana
5 Gregory
6 Denis
7 Erik
8 Helen
9 Izis
\$\endgroup\$
1
\$\begingroup\$

JavaScript (ES6), 78 83

Recursive solution. Being tail recursion, the variables t and r have not to be local.

f=x=>(t=r=0,x.replace(/\w/g,d=>t+=1+~-parseInt(d,r=36)%9),t>9?f(''+t):r?t:'*')

Explained

f=x=>(
  t = 0, // set initial value of counter to 0 
  r = 0, // flag to verify that we found at last one alphanumeric chars
  x.replace(/\w/g, d => ( // execute the following for each alphanumeric character
    // t += 1 + ~-parseInt(d,r=36) % 9 explained below
    r = 36, // set flag, could be any nonzero value
    d = parseInt(d,36), // convert to numeric. a..z -> 10..25, case insensitive.
    d = 1 + (d-1) % 9, // this is the arithmetic conversion required (
                       // works also with 0 because the strange implementation of % in javascript for negative numbers
    t = t + d // add to global counter
  ) ), 
  t > 9 // if t > 9 then we found some alphanumeric char, but we must repeat the loop on t
    ? f(''+t) // recursive call, t is numeric and must become a string
    : r // check flag r 
      ? t // if alphanumeric found, return t 
      : '*' // else return '*'
)

Test snippet

f=x=>(t=r=0,x.replace(/\w/g,d=>t+=1+~-parseInt(d,r=36)%9),t>9?f(''+t):r?t:'*')

console.log=x=>O.textContent+=x+'\n';

;[['13579',7],['Hello, world!',7],['00 0 00',0],['!@#$%^&*();','*'],
['3.141592',7],['3.1415926535897932384',7],
['Bob', 1],['Charlie', 2],['Anna', 3],['Fana', 4],['Gregory', 5],
['Denis', 6],['Erik', 7],['Helen', 8],['Izis', 9]]
.forEach(t=>{
  i=t[0]+''
  k=t[1]
  r=f(i)
  console.log(i+' -> ' + r + (k==r? ' OK':' Fail - expected '+k))
  })
<pre id=O></pre>

\$\endgroup\$
0
\$\begingroup\$

Python, 154 bytes

def A(S):
 D=lambda x:int(x)if x.isdigit()else (ord(x.lower())-6)%9
 while len(S)>1:S=str(sum([D(c)for c in S if c.isalnum()]))
 print S if int(S)else"*"
\$\endgroup\$
  • \$\begingroup\$ This fail test with 00 0 00! \$\endgroup\$ – F. Hauri Dec 25 '15 at 11:17
0
\$\begingroup\$

Mathematica, 133 bytes

f[s_]:= ToCharacterCode@ToUpperCase@s-64/.{a_/;17>-a>6:>a+16,b_/;b<1||b>26:>""}//If[Or@@NumberQ/@#,Tr@#/.""->0//.a_:>Tr@IntegerDigits@a,"*"]&

A bit different from LegionMammal978's above. My function turns everything into a character code, then filters out the non-alphanumeric things (replacing them with empty strings). If there are no alphanumerics, then it returns *, otherwise it takes the digital root. This could be significantly (~15B) shorter if I didn't have to deal with the all-zeros-string case. C'est la vie.

Mathematica magic, for the uninitiated: foo//.a_:>Tr@IntegerDigits@a is a repeated replacement: it replaces any numbers a in foo with the sum of their digits, then it replaces again until it hits a fixed point, i.e. a stops changing under replacement.

Tests:

f /@ {"13579", "Hello,world!", "00 0 00", "!@#$%^&*()", "3.141592","3.1415926535897932384"}
     => {7, 7, 0, "*", 7, 7}
f /@ {"Bob", "Charlie", "Anna", "Fana", "Gregory", "Denis", "Erik",  "Helen", "Izis"}
     => {1, 2, 3, 4, 5, 6, 7, 8, 9}
\$\endgroup\$
  • \$\begingroup\$ Unfortunely, I could'nt test your sample. Please post test cases \$\endgroup\$ – F. Hauri Dec 27 '15 at 8:31
  • \$\begingroup\$ On my way. While putting together the tests, I've also found some typos to fix. Thanks ;) \$\endgroup\$ – hYPotenuser Dec 27 '15 at 13:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.