20
\$\begingroup\$

Inspired by wezl.

Your challenge is to take words (sequences of [a-zA-Z]) and truncate them to length 3. For example, never gonna give you up would become nev gon giv you up.

Words will not necessarly be delimited by spaces - for example, youtube.com/watch?v=dQw4w9WgXcQ will become you.com/wat?v=dQw4w9WgX. Input will be printable ASCII.

This is , shortest wins!

Testcases

never gonna give you up -> nev gon giv you up
youtube.com/watch?v=dQw4w9WgXcQ -> you.com/wat?v=dQw4w9WgX
code golf -> cod gol
happy_birthday_to_you -> hap_bir_to_you
*hello world* -> *hel wor*
abcd1234fghi -> abc1234fgh
\$\endgroup\$
2

30 Answers 30

10
\$\begingroup\$

K (ngn/k), 21 20 bytes

-1 byte thanks to coltim!

{5>1(1+*)\~"a{"'_x}#

Try it online!

{ ... }# Filter the input using the boolean mask returned by the left function.
_x Convert the string to lowercase.
~"a{"' For each character, is it in ["a", "{")?
1(1+*)\ A scan that converts each 1 to the number of consecutive 1s up to it, incremented by 1. 0s are mapped to 1.
5> Keep the indices where this is at most 4.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ I think one byte can be trimmed with {5>1(1+*)\~"a{"'_x}# \$\endgroup\$
    – coltim
    Commented Jun 20, 2022 at 13:39
  • \$\begingroup\$ @coltim yes that works, thanks! Also saved two bytes on another answer \$\endgroup\$
    – ovs
    Commented Jun 20, 2022 at 13:47
  • \$\begingroup\$ What's the { in the "a{" string for? \$\endgroup\$ Commented Jun 20, 2022 at 13:56
  • \$\begingroup\$ @thejonymyster It marks the end of the interval of the alphabetical characters ({ is the character after z). Without it characters with a higher codepoint than z would be counted as alphabetical. "a{"' sorts each char in its input into 3 categories: characters that appear before a in ASCII get -1, before { get 0 and everything after that gets 1. ~ is boolean inversion on that result. \$\endgroup\$
    – ovs
    Commented Jun 20, 2022 at 14:10
9
\$\begingroup\$

x86-64 machine code, 29 bytes

83 C9 FF AC 88 07 2C 41 72 06 24 1F 3C 1A 72 02 31 C9 83 F9 FC 7E EC B0 00 AE E0 E7 C3

Try it online!

Following the standard calling convention for Unix-like systems (from the System V AMD64 ABI), this takes in RDI an address at which to place the result, as a null-terminated byte string; and the address of the input, as a null-terminated byte string, in RSI.

In assembly:

f:
    or ecx, -1
startloop:
    lodsb
    mov [rdi], al
    sub al, 0x41
    jb notletter
    and al, 0x1F
    cmp al, 26
    jb isletter
notletter:
    xor ecx, ecx
isletter:
    cmp ecx, -4
    jle startloop
    mov al, 0
    scasb
    loopnz startloop
    ret
\$\endgroup\$
7
\$\begingroup\$

Python, 52 bytes

lambda s,c=0:[i for i in s if(c:=-~c*i.isalpha())<4]

Attempt This Online!

Outputs as a list of characters.

If that's not allowed:

Python, 58 bytes

lambda s,c=0:"".join(i*((c:=-~c*i.isalpha())<4)for i in s)

Attempt This Online!

\$\endgroup\$
6
\$\begingroup\$

Zsh, 39 bytes

<<<${1//(#m)[a-zA-Z](#c3,)/$MATCH[1,3]}

Attempt This Online!

\$\endgroup\$
6
\$\begingroup\$

JavaScript (V8), 39 bytes

x=>x.replace(/(?<=[a-z]{3})[a-z]/gi,"")

Try it online!

Pretty straightforward.

x=>x.replace(/(?<=[a-z]{3})[a-z]/gi,"")

x=>x.replace(                      ,  )  // standard regex replace setup
             /                  /gi      // case insensitive, replace all occurences of
                           [a-z]         // an alphabetical character
                                    ""   // with the empty string
              (?<=        )              // given that it is immediately following
                  [a-z]{3}               // 3 consecutive alphabet characters
\$\endgroup\$
0
6
\$\begingroup\$

BQN, 22 bytes

⊣/˜3≥·(×+⊢)`2|"A[a{"⊸⍋

Try it here!

-5 bytes thanks to @ovs!

Explanation

  • ⊣/˜... filter input by constructing this binary array...
  • 2|"A[a{"⊸⍋ for each char, 1 if alphabetical and 0 otherwise
    • more literally: check if ordering the char into A[a{ will yield an odd index
  • (×+⊢)` count consecutive runs of 1s
  • 3≥· for each element, check if >= 3

Example of how the input is manipulated under the hood:

never gonna give you up  # input
11111011111011110111011  # 2|"A[a{"⊸⍋
12345012345012340123012  # (×+⊢)`
11100111100111101111111  # 3≥·
nev██ gon██ giv█ you up  # ⊣/˜
\$\endgroup\$
2
  • 2
    \$\begingroup\$ ∘∊⟜3‿1 can be shortened to 2|. And one more byte can be saved with (...)⊸/ -> ⊢/˜... \$\endgroup\$
    – ovs
    Commented Jun 18, 2022 at 20:38
  • 1
    \$\begingroup\$ @ovs thanks, very clever! That filter trick is very handy \$\endgroup\$ Commented Jun 19, 2022 at 4:50
5
\$\begingroup\$

brev, 81 bytes

(print (strse (read-line) '(+ alpha) (as-list (fn (take x (min (length x) 3))))))

I would've wanted:

(print (strse (read-line) 'word (as-list (fn (take x (min (length x) 3))))))

but that YouTube example looks differently.

Reading and writing stdin/out adds 13 bytes, it's just 68 as a function:

((over (strse x '(+ alpha) (as-list (fn (take x (min (length x) 3))))))
 '("never gonna give you up"
   "youtube.com/watch?v=dQw4w9WgXcQ"
   "code golf"
   "happy_birthday_to_you"
   "*hello world*"
   "abcd1234fghi"))
\$\endgroup\$
4
\$\begingroup\$

Vyxal s, 7 bytes

øWƛL3∵Ẏ

Try it Online!

Would be 5 bytes if not for modular indexing. Undone by something otherwise useful, I say!

Explained

øWƛL3∵Ẏ
øW      # Split the input on words (/[A-z]+/). Numbers and punctuation are left as single character strings.
  ƛ     # To each "word":
   L3∵  #   get the minimum of the length of the word and the number 3. This is needed to account for modular indexing. This would otherwise just be `3`
      Ẏ #   get the first amount of characters of the word, where the number of characters is the number we just calculated.
# the s flag joins everything into a single string before printing it.  
\$\endgroup\$
4
\$\begingroup\$

R, 38 bytes

\(x)gsub('([a-z]{3})[a-z]+','\\1',x,T)

Attempt This Online!

-2 bytes thanks to pajonk!

Direct application of gsub.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ You can omit the curly braces for -2 bytes. \$\endgroup\$
    – pajonk
    Commented Jun 18, 2022 at 18:49
4
\$\begingroup\$

Perl 5 -p, 17 bytes

s/\pL{3}\K\pL*//g

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Retina 0.8.2, 21 bytes

i`([a-z]{3})[a-z]+
$1

Try it online! Link includes test cases. Explanation: Case-insensitively matches runs of three letters followed by at least one more letter and keeps only the first three.

\$\endgroup\$
3
\$\begingroup\$

Python, 84 bytes

lambda s:''.join([w,w[:3]][w.isalpha()]for w in re.split('([^a-zA-Z])',s))
import re

Attempt This Online!

\$\endgroup\$
3
\$\begingroup\$

Factor, 58 50 48 bytes

[ R/ [a-z]+/i [ 3 short head ] re-replace-with ]

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Python, 57 bytes

lambda s:re.sub('([a-z]{3})[a-z]+',r'\1',s,0,2)
import re

Attempt This Online!

A straight port of my regex R solution. re.IGNORECASE is 2.

Framework taken from solid.py's answer.

\$\endgroup\$
3
\$\begingroup\$

Pip, 38 25 bytes

Simple regex solution, matches runs of letters and captures the first 3, replacing matches with the capture group.

aR-`([A-Z]{3})[A-Z]*``\1`

Attempt This Online!

\$\endgroup\$
1
  • \$\begingroup\$ You can use a callback function to save some bytes: either B (second function argument, which is the contents of the first capture group), or change the regex to [A-Z]{3,} and use _@<3 (first three characters of whole match). Also, the built-in regex variable XA is useful here. 15 bytes \$\endgroup\$
    – DLosc
    Commented Jul 11, 2022 at 18:56
3
\$\begingroup\$

C (clang), 78 65 bytes

c;*p;f(*s){for(c=0,p=s;*s;p+=c<4)c=iswalpha(*p=*s++)?c+1:0;*p=0;}

Try it online!

Saved a whopping 13 bytes thanks to Neil!!!

Inputs a wide character string.
Performs the word truncations in place.

\$\endgroup\$
2
  • \$\begingroup\$ I fiddled around with your loop a bit and ended up with for(c=0,p=s;*s;p+=c<4)c=iswalpha(*p=*s++)?c+1:0; which saves 13 bytes. \$\endgroup\$
    – Neil
    Commented Jun 18, 2022 at 21:19
  • \$\begingroup\$ @Neil That's awesome, hold the pointer at the same place until it has a valid char - thanks! :D \$\endgroup\$
    – Noodle9
    Commented Jun 18, 2022 at 22:03
3
\$\begingroup\$

Ruby, 31 bytes

->s{s.gsub(/[a-z]+/i){$&[0,3]}}

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ Nice one! Sad \pl doesn't work in Ruby... You can save a few bytes using -p too of you're that way inclined! Try it online! \$\endgroup\$ Commented Jun 20, 2022 at 19:48
3
\$\begingroup\$

Jelly, 10 bytes

=ŒskŻḣ€4FḊ

Try it online!

It's absolutely hideous, but the most elegant thing I've come up with is a byte longer: nŒs‘×¥\<4x@

    Ż         Prepend a 0 to the input, and
   k          partition that after indices where
=             the original input equals
 Œs           itself case-swapped.
     ḣ€4      Keep the first 4 elements of each slice (one non-letter + up to 3 letters).
        F     Flatten,
         Ḋ    and remove the 0.
\$\endgroup\$
2
\$\begingroup\$

Stax, 7 bytes

îªZ╛▲?↕

Run and debug it

\$\endgroup\$
2
\$\begingroup\$

Charcoal, 15 bytes

ΦθΦ⁴¬№α↥§⁺×_μθκ

Try it online! Link is to verbose version of code. Explanation: Generates all overlapping substrings of up to four characters ending at each character and keeps those containing at least one non-letter.

 θ              Input string
Φ               Filtered by
   ⁴            Literal integer `4`
  Φ             Any value satisfies
      α         Predefined variable uppercase alphabet
    ¬№          Does not contain
           _    Literal string `_`
          ×     Repeated by
            λ   Current value
         ⁺      Concatenated with
             θ  Input string
        §       Indexed by
              κ Current index
       ↥        Uppercase
                Implicitly print

Note that I use Filter instead of Any because the latter does not implicitly convert an integer to a range.

\$\endgroup\$
2
\$\begingroup\$

Pyth, 15 bytes

sf<=Z*hZ}rT0G4Q

Try it online!

Port of pxeger's Python answer

\$\endgroup\$
2
\$\begingroup\$

Julia, 46 bytes 42 bytes

f(s)=replace(s,r"([\p{L}]{3})[\p{L}]*"=>s"\1")

We do not need the square brackets, thx to @thejonymyster:

f(s)=replace(s,r"(\p{L}{3})\p{L}*"=>s"\1")

Attempt This Online!

\p{L}: https://stackoverflow.com/questions/3617797/regex-to-match-only-letters

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Are the []s around each \p{L} necessary? I don't know the language, but it seemed to not error when I removed them. \$\endgroup\$ Commented Jun 19, 2022 at 17:54
  • 1
    \$\begingroup\$ yes. i'm weak on regex-es, it was just a simple change the [a-zA-z] to the \p stuff. i suspect that the \p syntax can represent a character class w/o the square brackets :-) \$\endgroup\$ Commented Jun 19, 2022 at 19:47
  • 1
    \$\begingroup\$ you can save 2 bytes by using an anonymous function s->... or an unary operator !s=... \$\endgroup\$
    – MarcMush
    Commented Jun 23, 2022 at 18:08
  • \$\begingroup\$ @MarcMush: i leave this for the more ambitious golfers :-) \$\endgroup\$ Commented Jun 23, 2022 at 20:30
  • \$\begingroup\$ common, you can be ambitious too ^^ It's not that hard \$\endgroup\$
    – MarcMush
    Commented Jun 24, 2022 at 15:43
2
\$\begingroup\$

05AB1E, 12 bytes

.γa}DaÅÏ3£}J

Try it online or verify all test cases.

Explanation:

.γ }          # Adjacent group the (implicit) input-string by:
  a           #  Letters
    D         # Duplicate this list of groups
     a        # Check which groups only consist of letters
      ÅÏ  }   # Apply on the truthy indices:
        3£    #  Keep up to the first 3 characters
           J  # Join all groups back together to a string
              # (which is output implicitly as result)
\$\endgroup\$
2
\$\begingroup\$

Lua, 41 bytes

print(((...):gsub('(%a%a?%a?)%a*','%1')))

Try it online!

\$\endgroup\$
1
\$\begingroup\$

sed -E, 24 bytes

s/([A-Z]{3})[A-Z]*/\1/gi

Try it online!

\$\endgroup\$
1
\$\begingroup\$

Raku -p, 20 bytes

(formerly known as Perl_6)

s:g/<:L>**3<(<:L>*//

FYI, Raku regexes tolerate whitespace by default, so a more readable (exploded) version would be:

s:g/ <:L>**3 <( <:L>* //
\$\endgroup\$
1
  • \$\begingroup\$ @Sean, any suggestions? Also, how to make/get TIO link? \$\endgroup\$ Commented Oct 4, 2022 at 7:31
1
\$\begingroup\$

Rust, 109 bytes

fn f(s:&str)->String{let mut n=0;s.chars().filter(|c|if c.is_alphabetic(){n+=1;n<4}else{n=0;true}).collect()}

Try it online!

\$\endgroup\$
4
  • \$\begingroup\$ Idea: .filter(|c|{n+=1;if!c.is_alphabetic(){n=0};n<4}) \$\endgroup\$
    – lynn
    Commented Jun 22, 2022 at 18:21
  • \$\begingroup\$ After looking at the thread on Rust code golfing, a couple easy things: write the answer as a closure |s|->String{...}. And maybe, instead of the let mut write |s|->String((|mut n|...)(0)). (And before Lynn's suggestion, I was thinking: use 0<1 in place of true.) \$\endgroup\$ Commented Jun 22, 2022 at 23:14
  • \$\begingroup\$ So, down to 96 bytes: tio.run/… \$\endgroup\$ Commented Jun 22, 2022 at 23:33
  • \$\begingroup\$ OK, so it turned out in this case changing the let to a call of a closure didn't save any bytes: tio.run/… \$\endgroup\$ Commented Jun 23, 2022 at 0:26
1
\$\begingroup\$

J-uby, 29 bytes

Port of G B’s Ruby answer.

~(:gsub+(~:[]&3&0))&/[a-z]+/i

Attempt This Online!

Explanation

(~(:gsub +               )               # replace
                           & /[a-z]+/i)  # each word
           (~:[] & 3 & 0)                # with its first three characters
\$\endgroup\$
1
\$\begingroup\$

Scala, 54 bytes

Golfed version. Try it online!

def f(x:String)=x.replaceAll("(?<=[a-z]{3})[a-z]", "")
\$\endgroup\$
1
\$\begingroup\$

Uiua SBCS, 18 bytes

↘1▽<5\(+1×)≠0±.⊂@a

Try it!

↘1▽<5\(+1×)≠0±.⊂@a
               ⊂@a  # prepend letter a
           ≠0±.     # mask of letters
     \(+1×)         # multiply-increment scan
  ▽<5               # keep letters that correspond to less than 5 in scan
↘1                  # remove first letter
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.