Discussion
· Sep 13, 2022

[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?

Discussion (21)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;
    }
}

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

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.

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

If there was a built-in function for expanding the list into a table, then the solution would be even shorter.

 
An example of such a function:

Now you can do a variety of things with the string, for example:

select min(length(word)) "min",length(wordlen,word from dc_golf.Kata_split('bitcoin take over the world maybe who knows perhaps',' ')

Result:

min len word
3 7 bitcoin
3 4 take
3 4 over
3 3 the
3 5 world
3 5 maybe
3 3 who
3 5 knows
3 7 perhaps