﻿ [Code-Golf] Shortest Word | InterSystems Developer Community | Code Snippet
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?

2
0 316
Discussion (21)2

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
{
s t=\$zstrip(\$TR(t,..#WhiteSpace,",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"),"<=>",",")
q:'\$L(\$TR(t,",")) 0
s l=\$L(t,","),m=\$L(\$P(t,","))
f i=1:1:l {s n=\$L(\$P(t,",",i)) s:((n>0)&&(n<m)) m=n}
q m
}```

(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
{
s \$P(replace,",",\$L(..#WhiteSpace)+1)=""
s t=\$zstrip(\$TR(t,..#WhiteSpace,replace),"<=>",",")
q:'\$L(\$TR(t,",")) 0
s l=\$L(t,","),m=\$L(\$P(t,","))
f i=1:1:l {s n=\$L(\$P(t,",",i)) s:((n>0)&&(n<m)) m=n}
q 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(""))
}

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

In case, it's not a typo, tell me the secret of how to "shave symbols" OK, it was a typo and there is nothing to shave /// 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))
}

This code can be reduced by two more characters.

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

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(word) len,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