Discussion
Yone Moreno · Sep 13

[Code-Golf] Shortest Word

To celebrate programming day, could we create solutions for a little challenge?

We have the following statement:

Given a string of words, return the length of the shortest word(s).

String will never be empty and you do not need to account for different data types.

 

Some test made in Java would be:

import org.junit.Test;

import java.util.Arrays;
import java.util.Random;
import java.util.stream.Collectors;

import static org.junit.Assert.assertEquals;

/**
 * Created by Javatlacati on 01/03/2017.
 */
public class KataTest {
    @Test
    public void findShort() throws Exception {
        assertEquals(3, Kata.findShort("bitcoin take over the world maybe who knows perhaps"));
        assertEquals(3, Kata.findShort("turns out random test cases are easier than writing out basic ones"));

        assertEquals(3, Kata.findShort("lets talk about Java the best language"));
        assertEquals(1, Kata.findShort("i want to travel the world writing code one day"));
        assertEquals(2, Kata.findShort("Lets all go on holiday somewhere very cold"));
        assertEquals(2, Kata.findShort("Let's travel abroad shall we"));
    }

    public static int sol(String s) {
        return Arrays.stream(s.split(" ")).mapToInt(c -> c.length()).min().getAsInt();
    }

    String[] names = new String[]{"Bitcoin", "LiteCoin", "Ripple", "Dash", "Lisk", "DarkCoin", "Monero", "Ethereum", "Classic", "Mine", "ProofOfWork", "ProofOfStake", "21inc", "Steem", "Dogecoin", "Waves", "Factom", "MadeSafeCoin", "BTC"};

    @Test
    public void randomTests() throws Exception {
        Random r = new Random();
        int tam = r.nextInt(names.length);
        String a = Arrays.stream(names).unordered().skip(names.length - tam).collect(Collectors.joining(" "));
        assertEquals(sol(a), Kata.findShort(a));
    }
}

 

How would you implement this in ObjectScript?

 

 

 

As a reference I will reply to the original post with my solution in Java

 

How would you implement this in ObjectScript?

2
0 250
Discussion (19)2
Log in or sign up to continue

As a reference you could observe my following solution in Java:


 

import java.util.Arrays;

public class Kata {
    public static int findShort(String s) {
        String[] words = s.split(" ");
        int shortestLength = 0;
        for(String word : words){
          if(shortestLength == 0){
            shortestLength = word.length();
          }else{
            if(shortestLength > word.length()){
              shortestLength = word.length();
            }
          }  
        }
        return shortestLength;
    }
}

Not great around edge cases, but this passes the tests at least (and might beat a $find-based approach since it avoids set commands):

ClassMethod findShort(s) As %Integer
{
 f i=1:1 ret:$locate(s,"(^| )\w{"_i_"}( |$)") i
}

A small change (two bytes longer) to your solution makes it perfect, I think, for all cases

ClassMethod findShort(s) As %Integer
{
 f i=1:1 ret:$locate(" "_s_" "," [^ ]{"_i_"} ") i
}

BUT, and that's, what I want to say: this function is an awesome demonstration of economic nature of ObjectScript in comparison to Java. Your one-liner vs. a whole page of code... I love ObjectScript!

Sorry, that had to be said...

ClassMethod Short(t) As %Integer
{
  t=$TR(t,$C(9,10,13,32,160),",,,,,")
  q:'$L($TR(t,",")) 0
  l=$L(t,","),m=200
  i=1:1:{n=$L($P(t,",",i)) s:((n>0)&&(n<m)) m=n}
  m
}

In case, the shortest word is longer then  200 chars, than the result will be wrong. Instead of using a constant (200)

  s l=$L(t,","),m=200
  f i=1:1:l {s n=$L($P(t,",",i)) s:((n>0)&&(n<m)) m=n}

use a more generic approach

  s l=$L(t,","),m=$L($P(t,","))
  f i=2:1:l {s n=$L($P(t,",",i)) s:((n>0)&&(n<m)) m=n}

Also, removing the unnecessary delimiters (commas) simplifies the code too

ClassMethod Short(t) As %Integer
{
  s t=$zstrip($TR(t,$C(9,10,13,32,160),",,,,,"),"<=>",",")
  s m=$L($P(t,","))
  f i=2:1:$l(t,",") {s n=$L($P(t,",",i)) s:n<m m=n}
  q m
}

Thanks for recommendations. Taking these changes onboard think I would then make the whitespace characters configurable.

Then others can just override this behaviour in a subclass or maintain the original a bit easier.

Parameters are a great ObjectScript syntactic sugar :)

Parameter WhiteSpace = {$C(9,10,13,32,160)};

ClassMethod Short(t) As %Integer
{
  t=$zstrip($TR(t,..#WhiteSpace,",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"),"<=>",",")
  q:'$L($TR(t,",")) 0
  l=$L(t,","),m=$L($P(t,","))
  i=1:1:{n=$L($P(t,",",i)) s:((n>0)&&(n<m)) m=n}
  m
}

Some comments...

(1) you can generate a string with the necessary commas, (2) also, the variable "l" is unnecessary and (3) after assigning to "m" the length of the first word, start the for-loop with the next (second) word...

According to problem description, the string will never be empty, so an early "q 0" is also not necessary and for the case, this happens, the first piece of the string will have a length of 0.

Parameter WhiteSpace = {$C(9,10,13,32,160)};

ClassMethod Short(t) As %Integer
{
  s t=$zstrip($TR(t,..#WhiteSpace,$tr($j("",$l(..#WhiteSpace))," ",",")),"<=>",",")
  s m=$L($P(t,","))
  f i=2:1:$L(t,",") {s n=$L($P(t,",",i)) s:n<m m=n}
  q m
}

Sharing for readers, an alternative code approach, to generate a pad-string without Translate and Justify, uses another application of the Piece function.

set $Piece(replace,",",81)=""

This is sets a comma delimited string, at piece 81, to an empty string.

Which effectively results in a string of 80 commas.

So:

Parameter WhiteSpace = {$C(9,10,13,32,160)}; 

ClassMethod Short(t) As %Integer
{
  $P(replace,",",$L(..#WhiteSpace)+1)=""
  t=$zstrip($TR(t,..#WhiteSpace,replace),"<=>",",")
  q:'$L($TR(t,",")) 0
  l=$L(t,","),m=$L($P(t,","))
  i=1:1:{n=$L($P(t,",",i)) s:((n>0)&&(n<m)) m=n}
  m
}

Regarding input. I don't trust calling code to never provide an empty string :)

Example updated as feedback.

I assume, you want to change ALL ..#WhiteSpace chars to the delimiter, used by $piece()

  s $P(replace,",",$L(..#WhiteSpace) + 1)=""

I'm sure pattern matching can do better but no idea how:

ClassMethod findShort(s) As %Integer [ ProcedureBlock = 0 ]
{
 s s=" "_s_" " for i=1:1 {x "s q=(s?.E1P"_i_"A1P.E)" q:q} q i
}

You count the alpha-only words, "Let's, 21inc, ..." would't pass the check. So I changed your code to

ClassMethod findShort(s) As %Integer
{
 s s=" "_s_" " f i=1:1 { ret:s?@(".e1"" """_i_"anp1"" "".e") i }
}

I hope, it's OK...

ClassMethod MinLength(As %String) As %Integer
{
i=1:1:$L(s," "){v($L($P(s," ",i)))=iQuit $O(v(""))
}
 

In case, it's not a typo, tell me the secret of how to "shave symbols" devil

OK, it was a typo and there is nothing to shave smiley

/// 48 characters:
ClassMethod 
MinLength(As %String) As %Integer
{
 F i=1:1:$L(s){S v($L($P(s," ",i)))=i} Q $O(v(0))
}

Revisiting challenge in Erlang.

Tail recursive (no increase in stack height), single pass of string, using language primitives only.

-module(shortest_word).
-export([v/1]).

v([])->0;
v(T)->v(T,0,999).
v([],C,M) when C>0,M>C->C;
v([],_,M)->M;
v([32|T],C,M) when C>0,C<M->v(T,0,C);
v([32|T],_,M)->v(T,0,M);
v([_|T],C,M)->v(T,C+1,M).

--------------

>shortest_word:v("abc deff").

3

nice
but how does ERLANG relate to IRIS or Caché ?