String Distance

28

2

Challenge

Given an input of an all-lowercase string [a-z], output the total distance between the letters.

Example

Input: golf

Distance from g to o : 8
Distance from o to l : 3
Distance from l to f : 6

Output: 17

Rules

  • Standard loopholes forbidden
  • This is - shortest answer in bytes wins.
  • The alphabet can be traversed from either direction. You must always use the shortest path. (i.e the distance between x and c is 5).

1

Test cases

Input: aa
Output: 0

Input: stack
Output: 18

Input: zaza
Output: 3

Input: valleys
Output: 35

Daniel

Posted 2016-09-17T07:01:49.373

Reputation: 1 808

Answers

11

Jelly, 11 8 bytes

OIæ%13AS

Saved 3 bytes thanks to @Martin Ender.

Try it online! or Verify all test cases.

Explanation

OIæ%13AS  Input: string Z
O         Ordinal. Convert each char in Z to its ASCII value
 I        Increments. Find the difference between each pair of values
  æ%13    Symmetric mod. Maps each to the interval (-13, 13]
      A   Absolute value of each
       S  Sum
          Return implicitly

miles

Posted 2016-09-17T07:01:49.373

Reputation: 15 654

6I came across æ% while reading through the built-ins the other day, and it was pretty much made for this (type of) problem: OIæ%13AS – Martin Ender – 2016-09-17T15:44:53.013

I think this is 9 bytes (æ is two). – Aleksei Zabrodskii – 2016-09-18T09:48:22.387

1

@elmigranto Jelly has a codepage that encodes each of its characters in one byte: https://github.com/DennisMitchell/jelly/wiki/Code-page

– ruds – 2016-09-18T15:54:37.923

10

Haskell, 57 56 bytes

q=map$(-)13.abs
sum.q.q.(zipWith(-)=<<tail).map fromEnum

Usage example: sum.q.q.(zipWith(-)=<<tail).map fromEnum $ "valleys" -> 35.

How it works:

q=map$(-)13.abs                -- helper function.
                               -- Non-pointfree: q l = map (\e -> 13 - abs e) l
                               -- foreach element e in list l: subtract the
                               -- absolute value of e from 13

               map fromEnum    -- convert to ascii values
      zipWith(-)=<<tail        -- build differences of neighbor elements
  q.q                          -- apply q twice on every element
sum                            -- sum it up

Edit: @Damien saved one byte. Thanks!

nimi

Posted 2016-09-17T07:01:49.373

Reputation: 34 639

thanks for the rotational distance trick (q.q) – Leif Willerts – 2016-09-17T20:18:00.677

Wow nice one! You can add map in the definition of q for one byte less – Damien – 2016-09-21T11:21:23.550

@Damien: well spotted. Thanks! – nimi – 2016-09-21T15:42:32.643

8

MATL, 14, 10 bytes

dt_v26\X<s

Try it online!

Thanks @Suever for saving 4 bytes!

Explanation:

d           % Take the difference between consecutive characters
 t_         % Make a copy of this array, and take the negative of each element
   v        % Join these two arrays together into a matrix with height 2
    26\     % Mod 26 of each element
       X<   % Grab the minimum of each column
         s  % Sum these. Implicitly print

Previous version:

d26\t13>26*-|s

James

Posted 2016-09-17T07:01:49.373

Reputation: 54 537

6

Python 3, 69 68 bytes

lambda s:sum([13-abs(13-abs(ord(a)-ord(b)))for a,b in zip(s,s[1:])])

Breakdown:

lambda s:
         sum(                                                      )
             [                             for a,b in zip(s,s[1:])]
              13-abs(13-abs(ord(a)-ord(b)))

busukxuan

Posted 2016-09-17T07:01:49.373

Reputation: 2 728

1You can lose one byte by removing the space before for – Daniel – 2016-09-17T15:25:33.950

@Dopapp Oh yes, thanks! – busukxuan – 2016-09-17T15:26:11.567

2You could take the input as a list of characters and use recursion to save 3 bytes: f=lambda a,b,*s:13-abs(13-abs(ord(a)-ord(b)))+(s and f(b,*s)or 0) – Jonathan Allan – 2016-09-17T20:46:52.860

5

Java, 126 120 117 bytes

int f(String s){byte[]z=s.getBytes();int r=0,i=0,e;for(;++i<z.length;r+=(e=(26+z[i]-z[i-1])%26)<14?e:26-e);return r;}

Thanks to @KevinCruijssen for pointing out a bug in the original version and suggesting to make the for-loop empty.

The use of (26 + z[i] - z[i - 1]) % 26) is inspired from a comment by @Neil on another answer. (26 + ...)%26 serves the same purpose as Math.abs(...) because of ...? e : 26 - e.

Ungolfed:

int f(String s) {
    byte[]z = s.getBytes();
    int r = 0, i = 0, e;
    for (; ++i < z.length; r += (e = (26 + z[i] - z[i - 1]) % 26) < 14 ? e : 26 - e);
    return r;
}

todeale

Posted 2016-09-17T07:01:49.373

Reputation: 353

Welcome to the site! What language is this? How many characters/bytes is it? You should [edit] those details into the top of your post, with this markdown:#Language, n bytes` – James – 2016-09-17T19:12:11.747

Ok. Thanks. I've edited it. Any improvement? :) – todeale – 2016-09-17T19:16:14.960

Yes, much better. :) – James – 2016-09-17T19:19:05.633

1You're missing a - before an e in your ungolfed version. – Neil – 2016-09-19T09:02:07.550

Oh...thanks @Neil. It's fixed now. – todeale – 2016-09-20T13:36:13.623

2Welcome to PPCG! Hmm, I'm getting a "Type mismatch: cannot convert from int to byte" error at e=z[i]-z[i-1]; So you either need a cast to (byte) or change the e to int. Also, you can remove the for-loop brackets by placing everything inside the for-loop, like this: int f(String s){byte[]z=s.getBytes();int r=0,i=0,e;for(;++i<z.length;r+=(e=z[i]-z[i-1])>0?e<14?e:26-e:-e<14?-e:e+26);return r;} (PS: The reversed the for-loop is unfortunately the same length: int f(String s){byte[]z=s.getBytes();int r=0,i=z.length-1,e;for(;i>0;r+=(e=z[i]-z[--i])>0?e<14?e:26-e:-e<14?-e:e+26);return r;}. – Kevin Cruijssen – 2016-09-20T13:55:55.980

1Thanks a lot @KevinCruijssen :D. Your suggestion has helped a lot. – todeale – 2016-09-20T15:00:01.327

3

JavaScript (ES6), 84 82 79 bytes

Saved 3 bytes thanks to Cyoce:

f=([d,...s],p=parseInt,v=(26+p(s[0],36)-p(d,36))%26)=>s[0]?f(s)+(v>13?26-v:v):0

Explanation:

f=(
  [d,...s],                    //Destructured input, separates first char from the rest
  p=parseInt,                  //p used as parseInt
  v=(26+p(s[0],36)-p(d,36))%26 //v is the absolute value of the difference using base 36 to get number from char
  )
)=>
  s[0]?                        //If there is at least two char in the input
    f(s)                       //sum recursive call
    +                          //added to
    (v>13?26-v:v)              //the current shortest path
  :                            //else
    0                          //ends the recursion, returns 0

Example:
Call: f('golf')
Output: 17


Previous solutions :

82 bytes thanks to Neil:

f=([d,...s],v=(26+parseInt(s[0],36)-parseInt(d,36))%26)=>s[0]?f(s)+(v>13?26-v:v):0

84 bytes:

f=([d,...s],v=Math.abs(parseInt(s[0],36)-parseInt(d,36)))=>s[0]?f(s)+(v>13?26-v:v):0

Hedi

Posted 2016-09-17T07:01:49.373

Reputation: 1 857

1Instead of Math.abs(...) you can use (26+...)%26; this works because you're flipping values above 13 anyway. (I think this is how the MATL answer works.) – Neil – 2016-09-17T10:44:32.370

1Save some bytes by prepending the code with p=parseInt; and then using p() instead of parseInt() – Cyoce – 2016-09-20T15:21:28.400

3

Ruby, 73 bytes

->x{eval x.chars.each_cons(2).map{|a,b|13-(13-(a.ord-b.ord).abs).abs}*?+}

cia_rana

Posted 2016-09-17T07:01:49.373

Reputation: 441

2

PHP, 93 Bytes

for(;++$i<strlen($s=$argv[1]);)$r+=13<($a=abs(ord($s[$i-1])-ord($s[$i])))?$a=26-$a:$a;echo$r;

Jörg Hülsermann

Posted 2016-09-17T07:01:49.373

Reputation: 13 026

2

05AB1E, 12 bytes

SÇ¥YFÄ5Ø-}(O

Explanation

SÇ                   # convert to list of ascii values
  ¥                  # take delta's
   YF    }           # 2 times do
     Ä5Ø-            # for x in list: abs(x) - 13
          (O         # negate and sum

Try it online!

Emigna

Posted 2016-09-17T07:01:49.373

Reputation: 50 798

It's 12 symbols, not bytes. Byte-length would be 16 for UTF-8. – Aleksei Zabrodskii – 2016-09-18T09:49:26.767

@elmigranto: Indeed. In UTF-8 that would be the case, but 05AB1E uses CP-1252 where this is 12 bytes. – Emigna – 2016-09-18T10:06:33.057

2

Racket 119 bytes

(λ(s)(for/sum((i(sub1(string-length s))))(abs(-(char->integer
(string-ref s i))(char->integer(string-ref s(+ 1 i)))))))

Testing:

(f "golf")

Output:

17

Detailed version:

(define(f s)
  (for/sum((i(sub1(string-length s))))
    (abs(-(char->integer(string-ref s i))
          (char->integer(string-ref s(+ 1 i)))))))

rnso

Posted 2016-09-17T07:01:49.373

Reputation: 1 635

You could replace (define(f s) with (lambda(s), 2 bytes shorter (anonymous functions are fine). – fede s. – 2016-09-17T21:15:43.350

1Wait, Racket should take (λ(s) too, which if in utf8 is 6 bytes i think – fede s. – 2016-09-17T21:17:45.783

Done that. Thanks. – rnso – 2016-09-17T23:27:29.903

2

Perl, 46 bytes

Includes +3 for -p (code contains ')

Give input on STDIN without final newline:

echo -n zaza | stringd.pl

stringd.pl:

#!/usr/bin/perl -p
s%.%$\+=13-abs 13-abs ord($&)-ord$'.$&%eg}{

Ton Hospel

Posted 2016-09-17T07:01:49.373

Reputation: 14 114

2

Actually, 21 bytes

Based partially on cia_rana's Ruby answer.

There was a bug with O (in this case, map ord() over a string) where it would not work with d (dequeue bottom element) and p (pop first element) without first converting the map to a list with #. This bug has been fixed, but as that fix is newer than this challenge, so I've kept # in.

Edit: And the byte count has been wrong since September. Whoops.

Golfing suggestions welcome. Try it online!

O#;dX@pX♀-`A;úl-km`MΣ

Ungolfing

         Implicit input string.
          The string should already be enclosed in quotation marks.
O#       Map ord() over the string and convert the map to a list. Call it ords.
;        Duplicate ords.
dX       Dequeue the last element and discard it.
@        Swap the with the duplicate ords.
pX       Pop the last element and discard it. Stack: ords[:-1], ords[1:]
♀-       Subtract each element of the second list from each element of the first list.
          This subtraction is equivalent to getting the first differences of ords.
`...`M   Map the following function over the first differences. Variable i.
  A;       abs(i) and duplicate.
  úl       Push the lowercase alphabet and get its length. A golfy way to push 26.
  -        26-i
  k        Pop all elements from stack and convert to list. Stack: [i, 26-i]
  m        min([i, 26-i])
Σ        Sum the result of the map.
         Implicit return.

Sherlock9

Posted 2016-09-17T07:01:49.373

Reputation: 11 664

2

C#, 87 85 bytes

Improved solution - replaced Math.Abs() with the add & modulo trick to save 2 bytes:

s=>{int l=0,d,i=0;for(;i<s.Length-1;)l+=(d=(s[i]-s[++i]+26)%26)>13?26-d:d;return l;};

Initial solution:

s=>{int l=0,d,i=0;for(;i<s.Length-1;)l+=(d=Math.Abs(s[i]-s[++i]))>13?26-d:d;return l;};

Try it online!

Full source, including test cases:

using System;

namespace StringDistance
{
    class Program
    {
        static void Main(string[] args)
        {
            Func<string,int>f= s=>{int l=0,d,i=0;for(;i<s.Length-1;)l+=(d=Math.Abs(s[i]-s[++i]))>13?26-d:d;return l;};

            Console.WriteLine(f("golf"));   //17
            Console.WriteLine(f("aa"));     //0
            Console.WriteLine(f("stack"));  //18
            Console.WriteLine(f("zaza"));   //3
            Console.WriteLine(f("valleys"));//35
        }
    }
}

adrianmp

Posted 2016-09-17T07:01:49.373

Reputation: 1 592

1

Java 7,128 bytes

 int f(String s){char[]c=s.toCharArray();int t=0;for(int i=1,a;i<c.length;a=Math.abs(c[i]-c[i++-1]),t+=26-a<a?26-a:a);return t;}

Ungolfed

 int f(String s){
 char[]c=s.toCharArray();
 int t=0;
 for(int i=1,a;
     i<c.length;
   a=Math.abs(c[i]-c[i++-1]),t+=26-a<a?26-a:a);
return t;
 }

Numberknot

Posted 2016-09-17T07:01:49.373

Reputation: 885

1

Pyth, 20 bytes

Lm-13.adbsyy-M.:CMQ2

A program that takes input of a quoted string on STDIN and prints the result.

Try it online

How it works

Lm-13.adbsyy-M.:CMQ2  Program. Input: Q
L                     def y(b) ->
 m      b              Map over b with variable d:
  -13                   13-
     .ad                abs(d)
                CMQ   Map code-point over Q
              .:   2  All length 2 sublists of that
            -M        Map subtraction over that
          yy          y(y(that))
         s            Sum of that
                      Implicitly print

TheBikingViking

Posted 2016-09-17T07:01:49.373

Reputation: 3 674

1

C#, 217 bytes

Golfed:

IEnumerable<int>g(string k){Func<Char,int>x=(c)=>int.Parse(""+Convert.ToByte(c))-97;for(int i=0;i<k.Length-1;i++){var f=x(k[i]);var s=x(k[i+1]);var d=Math.Abs(f-s);yield return d>13?26-Math.Max(f,s)+Math.Min(f,s):d;}}

Ungolfed:

IEnumerable<int> g(string k)
{
  Func<Char, int> x = (c) => int.Parse("" + Convert.ToByte(c)) - 97;
  for (int i = 0; i < k.Length - 1; i++)
  {
    var f = x(k[i]);
    var s = x(k[i + 1]);
    var d = Math.Abs(f - s);
    yield return d > 13 ? 26 - Math.Max(f, s) + Math.Min(f, s) : d;
  }
}

Output:

aa: 0
stack: 18
zaza: 3
valleys: 35

'a' is 97 when converted to bytes, so 97 is subtracted from each one. If the difference is greater than 13 (ie, half of the alphabet), then subtract the differences between each character (byte value) from 26. A last minute addition of "yield return" saved me a few bytes!

Pete Arden

Posted 2016-09-17T07:01:49.373

Reputation: 1 151

1Two useless whitespaces: both before 's'. – Yytsi – 2016-10-27T17:03:50.573

1

dc + od, 65 bytes

od -tuC|dc -e'?dsN0sT[lNrdsNr-d*vdD[26-]sS<Sd*vlT+sTd0<R]dsRxlTp'

Explanation:

Because in dc you can't access a string's characters, I used od to get the ASCII values. These will be processed in reverse order from the stack (LIFO container) like so:

dsN0sT             # initialize N (neighbor) = top ASCII value, and T (total) = 0
[lNrdsNr-          # loop 'R': calculate difference between current value and N,
                   #updating N (on the first iteration the difference is 0)
   d*vdD[26-]sS<S  # get absolute value (d*v), push 13 (D) and call 'S' to subtract
                   #26 if the difference is greater than 13
   d*vlT+sT        # get absolute value again and add it to T
d0<R]dsR           # repeat loop for the rest of the ASCII values
xlTp               # the main: call 'R' and print T at the end

Run:

echo -n "golf" | ./string_distance.sh

Output:

17

seshoumara

Posted 2016-09-17T07:01:49.373

Reputation: 2 878

1

C, 82 86 83 76 bytes

t,u;f(char*s){for(t=0;*++s;u=*s-s[-1],t+=(u=u<0?-u:u)>13?26-u:u);return t;}

Assumes input string is at least one character long. This doesn't require #include<stdlib.h>

Edit: Argh, sequence points!

Try it on Ideone

ceilingcat

Posted 2016-09-17T07:01:49.373

Reputation: 5 503

in ideone compiler the string "nwlrbb" and all the rand string i try 6 len return all 0 but it seems not 0 the result.... – RosLuP – 2016-09-23T08:56:55.387

yes now it seems ok... – RosLuP – 2016-09-23T23:02:19.253

1

Scala, 68 bytes

def f(s:String)=(for(i<-0 to s.length-2)yield (s(i)-s(i+1)).abs).sum

Criticism is welcome.

Himself12794

Posted 2016-09-17T07:01:49.373

Reputation: 61

1

C, 70 bytes 76 bytes

k,i;f(char *s){for(i=0;*++s;i+=(k=abs(*s-s[-1]))>13?26-k:k);return i;}

NoSeatbelts

Posted 2016-09-17T07:01:49.373

Reputation: 89

0

Python 3, 126 bytes

With list incomprehension.

d=input()
print(sum([min(abs(x-y),x+26-y)for x,y in[map(lambda x:(ord(x)-97),sorted(d[i:i+2]))for i in range(len(d))][:-1]]))

edelbitter

Posted 2016-09-17T07:01:49.373

Reputation: 61

Nice answer. You could replace abs(x-y) by y-x since the call to sorted make x < y. – todeale – 2016-09-24T07:58:51.823

0

PHP, 79 bytes

for($w=$argv[1];$w[++$i];)$s+=13-abs(13-abs(ord($w[$i-1])-ord($w[$i])));echo$s;

Titus

Posted 2016-09-17T07:01:49.373

Reputation: 13 814

0

Java, 109 bytes

int f(String s){int x=0,t,a=0;for(byte b:s.getBytes()){t=a>0?(a-b+26)%26:0;t=t>13?26-t:t;x+=t;a=b;}return x;

Ekeko

Posted 2016-09-17T07:01:49.373

Reputation: 111