Written by

Discussion Yone Moreno · 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?

Comments

Yone Moreno · Sep 13, 2022

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


 

import java.util.Arrays;

public classKata{
    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;
    }
}
0
Timothy Leavitt · Sep 13, 2022

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
}
0
Julius Kavay  Sep 13, 2022 to Timothy Leavitt

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

0
Alex Woodhead · Sep 13, 2022

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
}

0
Julius Kavay  Sep 13, 2022 to Alex Woodhead

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

sl=$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

sl=$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
}
0
Alex Woodhead  Sep 13, 2022 to Julius Kavay

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}
0
Julius Kavay  Sep 13, 2022 to Alex Woodhead

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
}
0
Alex Woodhead  Sep 14, 2022 to Julius Kavay

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.

0
Julius Kavay  Sep 14, 2022 to Alex Woodhead

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

s$P(replace,",",$L(..#WhiteSpace) + 1)=""
0
Eduard Lebedyuk · Sep 13, 2022

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

ClassMethod findShort(s) As%Integer [ ProcedureBlock = 0 ]
{
 ss=" "_s_" "for i=1:1 {x"s q=(s?.E1P"_i_"A1P.E)"q:q} q i
}
0
Julius Kavay  Sep 13, 2022 to Eduard Lebedyuk

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
{
 ss=" "_s_" " f i=1:1 { ret:s?@(".e1"" """_i_"anp1"" "".e") i }
}

I hope, it's OK...

0
Yaron Munz · Sep 13, 2022

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

0
Eduard Lebedyuk  Sep 13, 2022 to Yaron Munz

I know how to shave 3 symbols from that...

0
Julius Kavay  Sep 13, 2022 to Eduard Lebedyuk

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

0
Julius Kavay  Sep 13, 2022 to Alex Woodhead

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

0
Alexey Maslov  Sep 14, 2022 to Julius Kavay

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

0
Vitaliy Serdtsev  Oct 19, 2022 to Alexey Maslov

This code can be reduced by two more characters.

0
Alex Woodhead · Sep 19, 2022

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

0
Robert Cemper  Sep 19, 2022 to Alex Woodhead

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

0
Vitaliy Serdtsev · Oct 19, 2022

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:

Class dc.golf.Kata Abstract ]
{

Query split(
  As %VarString,
  delim As %VarStringAs %Query(ROWSPEC "word:%String") [ SqlProc ]
{
}

ClassMethod splitExecute(
  ByRef qHandle As %Binary,
  As %VarString,
  delim As %VarStringAs %Status
{
  ##class(%ListOfDataTypes).BuildValueArray($lfs(s,delim),.qHandle)
  qHandle=0
  q $$$OK
}

ClassMethod splitFetch(
  ByRef qHandle As %Binary,
  ByRef Row As %List,
  ByRef AtEnd As %Integer 0As %Status PlaceAfter = splitExecute ]
{
  d=$o(qHandle(qHandle),1,v)
  d="" AtEnd=1 else Row=$lb(v),qHandle=}
  q $$$OK
}
}

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
0