15
\$\begingroup\$

Given two strings: a string s and an alphabet a, implement string projection in the shortest code possible.

String projection returns a string o that contains the characters in s that are in a. The order of the characters in o must match the order of characters in s. So if s = "abcd" and a = "12da34", o = "ad", since only "ad" is shared between s and a.

You will need to handle when a is empty (outputs empty string, since no character in s can match empty string).

Test Cases

"string", "alphabet" => "output"
-------------------------------
"abcd", "12da34" => "ad"
"hello, world!", "aeiou" => "eoo"
"hello, world!", "abcdefghijklmnopqrstuvwxyz" => "helloworld"
"Hello, World!", "abcdefghijklmnopqrstuvwxyz" => "elloorld" (case sensitivity)
"Hello, World!", "abcdef" => "ed"
"Hello, World!", "!,. \n\t" => ", !" (newline, tab)
"172843905", "abc123" => "123"
"fizzbuzz", "" => ""
"fizzbuzz", "fizzbuzz" => "fizzbuzz"
"", "fizzbuzz" => ""
"fizzbuzz", "zzzzz" => "zzzz"
"", "" => "" (two empty strings)

Winning Criteria

This is , so shortest code wins!

\$\endgroup\$
2
  • \$\begingroup\$ Surprised by how many golflangs have a builtin or two for this task \$\endgroup\$ Aug 10 at 8:51
  • \$\begingroup\$ prolog builtin is intersection/3 \$\endgroup\$
    – Razetime
    Sep 7 at 14:16

39 Answers 39

10
\$\begingroup\$

Rust, 20 bytes

str::matches::<&[_]>

Attempt This Online!

This mixes up three I/O methods for strings: the first parameter is a plain string slice &str, the second is a slice of chars &[char], and the output is an iterator yielding single-char string slices impl Iterator<Item=&str>.

Doc for str::matches. It can take several kinds of patterns (i.e. anything that implements Pattern) for non-overlapping substring search:

  • single char: matches that char
  • a &str or equivalent: matches that exact string
  • a slice of chars &[char] or equivalent: matches any char out of the ones listed
  • (and any 3rd party type that implements Pattern, e.g. Regex)

We use the third variety in this challenge, which is indicated by the ::<&[_]> part. _ is a kind of wildcard type, and it resolves to char by the compiler because &[char] is the only type matching &[_] that implements Pattern.

\$\endgroup\$
8
\$\begingroup\$

Jelly, 1 byte

f

Try it online!

This is exactly what Jelly's "filter-keep" dyadic atom does - takes a list on the left and a list on the right and keeps those in the left that appear in the right.

\$\endgroup\$
7
\$\begingroup\$

Haskell, 16 bytes

filter.flip elem

Try it online!

Takes arguments in reverse order.

\$\endgroup\$
7
\$\begingroup\$

Regex (ECMAScript or better), 20 bytes

s/(.)(?!.*␀.*\1)//sg

Try it online! - ECMAScript 2018
Try it online! - Perl
Try it online! - PCRE2
Try it online! - Boost
Try it online! - Python
Try it online! - Ruby
Try it online! - .NET

This is a single regex substitution, to be applied once. Input is taken in the form of the two strings delimited by NUL (ASCII 0).

Above and below, represents what is actually a raw NUL character in the regex.

s/              # Begin substitution - match the following:
      (.)       # \1 = one character
      (?!       # Negative lookahead - match if the following can't match:
          .*    # Skip over as many characters as possible, minimum zero, to make
                # the following match:
          ␀     # Skip over the NUL delimiter
          .*    # Skip over as many characters as possible, minimum zero, to make
                # the following match:
          \1    # Match the character we captured in \1
      )
/               # Substitution - replace with the following:
                # Empty string
/               # Flags:
s               # single line - "." will match anything, including newline
g               # global - find and replace all matches, going from left to right

This automatically erases the NUL and everything following it, because NUL and all characters following it are themselves not followed by NUL, so the negative lookahead matches for each of them.

(More test harnesses to come.)

As far as ECMAScript goes, this requires ECMAScript 2018 (aka ES9) due to the use of the s flag.

In Ruby, the m flag is used, which is the Ruby equivalent of what is s in most other regex engines.

\$\large\textit{Anonymous functions}\$

Perl, 42 bytes

sub{$_=join'␀',@_;s/(.)(?!.*␀.*\1)//sg;$_}

Try it online!

Takes the two strings as arguments.

Beaten by a 35 byte solution based on an unposted solution by Sisyphus: Try it online!

Ruby, 42 41 bytes

-1 byte thanks to Steffan

->s,a{(s+?␀+a).gsub /(.)(?!.*␀.*\1)/m,''}

Try it online!

PowerShell, 42 bytes

$args-join'␀'-creplace'(?s)(.)(?!.*␀.*\1)'

Try it online!

Takes the two strings as arguments.

JavaScript (ES9), 43 bytes

a=>a.join`␀`.replace(/(.)(?!.*␀.*\1)/sg,'')

Try it online!

Takes a list containing the two strings.

Java, 52 bytes

s->a->(s+"␀"+a).replaceAll("(?s)(.)(?!.*␀.*\\1)","")

Attempt This Online!

\$\large\textit{Full programs}\$

Perl, 37 bytes

$_=join'',<>;s/(.)(?!.*␀.*\1)//sg;say

Try it online!

Takes multiline input (terminated by EOF) using NUL as a delimiter between the two strings.

Beaten by a 19 byte solution based on an unposted solution by Sisyphus: Try it online! (they should really post it), or 25 bytes to insert chomp; at the beginning if being picky about the output being followed by a NUL.

\$\endgroup\$
5
\$\begingroup\$

R, 15 bytes

\(s,a)s[s%in%a]

Attempt This Online!

Takes input and outputs through vectors of character codes (as in linked test suite) or vectors of characters.

\$\endgroup\$
5
\$\begingroup\$

Brachylog, 4 bytes

dᵗ∋ᵛ

Try it online!

Takes a list [s, a], and generates a list constituting the projection.

dᵗ      Deduplicate a,
  ∋ᵛ    then yield some c from a pair of elements [c, c] from s and a.

Deduplicating a is necessary in order to not generate multiple pairs from the same element of s.

\$\endgroup\$
4
\$\begingroup\$

K (ngn/k), 4 bytes

Uses set difference (^) to compute the intersection: x∩y = x-(x-y)

^/^\

Try it online!

\$\endgroup\$
4
\$\begingroup\$

C (clang), 54 53 46 bytes

f(*s,*a){for(;*s;)write(1,s,!!index(a,*s++));}

Try it online!

Explanation

int*a

we're only using the value of the pointer, so anything with the size of a pointer would work here, int* was the shortest type.

for(;*s;)

while we're not at the end of the string.

!!index(a,*s++)

if the character *s is found in string (const char *) a, return a pointer to it's occurrence, otherwise, return NULL, !! makes it 1 or 0 respectively, which is the number of bytes we'll write.

write(1,s,...);

write to file descriptor 1, ... (0 or 1) bytes starting from s and increment s.

Caveat

There is undefined behavior in the form of an unsequenced modification and access to s, but clang happened to evaluate s (the second parameter to write) before incrementing it in the call to index(3) so in this case it works.

\$\endgroup\$
3
  • \$\begingroup\$ 45 bytes \$\endgroup\$
    – jdt
    Aug 19 at 12:51
  • \$\begingroup\$ sizeof a needs to be 8, otherwise it would segfault if the string is on the stack, or any address that doesn't fit in an int \$\endgroup\$
    – c--
    Aug 19 at 14:30
  • \$\begingroup\$ You can change it to f(*s,*a)(... if you really want to. \$\endgroup\$
    – jdt
    Aug 19 at 14:37
3
\$\begingroup\$

Factor, 6 bytes

within

Try it online!

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

Pip, 5 bytes

a@X^b

Try It Online!

Explanation

    b  Second command-line argument
   ^   Split into characters
  X    Convert to regex that matches any of those characters
 @     Find all matches of the regex in
a      First command-line argument

By default, the list of matches is concatenated together and output.

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

Dyalog APL, 1 byte

Attempt This Online!

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

sh + coreutils, 6 byes

tr -cd

Takes string s from stdin, alphabet a as an argument.

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

PARI/GP, 27 bytes

f(a,b)=[c|c<-a,[d==c|d<-b]]

Attempt This Online!

Input and output lists of characters.

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

05AB1E, 1 byte

Ã

Another 1-byte builtin. Takes the inputs in the order alphabet,string.

Try it online or verify all test cases.

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

C#, 33 bytes

(s,t)=>s.Where(c=>t.Contains(c));

Try it online!

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

CJam, 10 bytes

Loosely inspired by this answer to another challenge.

l~:A;{A&}/

Input is a line containing the two strings separated by a space. If a string contains special characters it needs to be defined explicitly as an array of chars.

Try it online! Or verify all test cases.

How it works

l~          e# Read line and evaluate: pushes the two strings onto the stack
  :A;       e# Copy the second string into variable A, then pop
     {  }/  e# For each character in the first string, do the following
            e# Implicitly push current character of the first string
      A     e# Push the second string
       &    e# Set intersection. This gives the current char or an empty string
            e# Implicitly display stack contents
\$\endgroup\$
2
\$\begingroup\$

BQN, 3 bytes

∊/⊣

Try it at BQN REPL

 /      # Replicate elements of 
  ⊣     # left argument
        # by
∊       # 1 if each element of left argument is in right argument
        # 0 otherwise
\$\endgroup\$
2
\$\begingroup\$

Python, 35 28 bytes

lambda a,b:filter(b.count,a)

Attempt This Online!

Takes in either strings or lists of characters, outputs a list of characters.


-7 bytes from @dingledooper by using filter + count instead of list comprehension and in

\$\endgroup\$
4
  • 4
    \$\begingroup\$ 28 bytes \$\endgroup\$ Aug 7 at 21:32
  • \$\begingroup\$ Don't think it works, @dingledooper: ATOL. Think that list comprehension was right, tho. \$\endgroup\$ Aug 14 at 0:52
  • \$\begingroup\$ After correcting the "filter-count" method to join the characters into a string, it comes out at 37 chars. \$\endgroup\$ Aug 14 at 1:06
  • 1
    \$\begingroup\$ @dsillman2000 filter returns a generator, so it doesn't work unless you unpack it as a list, such as with [*a]. And according to meta, it's considered an accepted form of output. \$\endgroup\$ Aug 14 at 1:12
1
\$\begingroup\$

Haskell, 20 bytes

a#b=filter(`elem`b)a
\$\endgroup\$
1
\$\begingroup\$

Rust, 70 bytes

|a:&str,b:&[u8]|a.bytes().filter(|d|b.contains(d)).collect::<Vec<_>>()

Attempt This Online!

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

Vyxal r, 3 bytes

F¹F

Try it Online!

Unfortuantely, the builtin doesn't work with duplicates.

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

Knight, 63 bytes

;=aP;=bP Wa;=c b;=dF;Wc;I?AcAa=dT0=cGc 1Lc;IdO+A Aa"\"0=aGa 1La

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ You didn't edit this into the list of answers. \$\endgroup\$
    – Aiden Chow
    Aug 8 at 20:54
1
\$\begingroup\$

Wolfram Language (Mathematica), 16 bytes

Cases[#|##&@@#]&

Try it online!

Input and output two lists of characters [alphabet][string].

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

MATLAB, 25 bytes

f=@(a,b)a(ismember(a,b))

Technically, only the call of a(ismember(a,b)) is required to fulfill the task, but to make it a callable function a function handle is created. Also, the inputs have to be of type char, not string, as a char is an array whereas a string is more like a complete unit in MATLAB.

Try it online!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ You don't need to count f= (an anonymous function is valid) or the final linefeed: tio.run/##y08uSSxL/… \$\endgroup\$
    – Luis Mendo
    Aug 8 at 21:37
1
\$\begingroup\$

JavaScript (Node.js), 44 bytes

s=>a=>[...s].filter(c=>a.includes(c)).join``

Try it online!

Unfortunately, using .filter(a.includes) for 38 (wow!) throws String.prototype.includes called on null or undefined

\$\endgroup\$
2
  • 1
    \$\begingroup\$ The output from that test harness is a bit hard to read, so for convenience: Try it online! \$\endgroup\$
    – Deadcode
    Aug 8 at 19:38
  • 1
    \$\begingroup\$ And in any case, nice answer! I find it quite interesting how close in length it is to my regex one. \$\endgroup\$
    – Deadcode
    Aug 8 at 19:41
1
\$\begingroup\$

Haskell + hgl, 5 bytes

fl<fe

Attempt This Online!

Pretty straight forward. fl filters fe checks if something is an element. Ends up being the same as the vanilla Haskell answer.

Reflection

This is a very simple task so there's not a lot to be said but:

  • I'm not sure why e was given the shorter name than fe. fe is almost certainly the more useful of the two and probably they should be swapped.
  • Since this is such a simple task, hgl should probably follow the lead of the golfing langs and make this a builtin.
  • There are a bunch of builtins that are similar to this task like nx, but the descriptions were not the clearest.
\$\endgroup\$
1
\$\begingroup\$

Nibbles, 2 bytes (4 nibbles)

|@?@
|       # filter
 @      # the (second) input array
        # for truthy results of the function:
  ?     # index (or 0 if not found) 
   @    # in the (first) input array

enter image description here

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

Charcoal, 5 bytes

Φθ№ηι

Try it online! Link is to verbose version of code. Explanation: Trivial port of @dingledooper's golf to adam's Python answer.

 θ      First input
Φ       Filtered where
  №     Count of
    ι   Current character
   η    In second input
        Implicitly print

Newlines and other unprintables need to be entered using JSON format.

\$\endgroup\$
2
  • \$\begingroup\$ 30 bytes or 5 bytes? \$\endgroup\$ Aug 9 at 11:26
  • \$\begingroup\$ @DominicvanEssen Whoops, I'm trying to do this on a tablet and forgot to update from the verbose to the succinct byte count. \$\endgroup\$
    – Neil
    Aug 9 at 11:44
1
\$\begingroup\$

Raku, 22 bytes

{$^a.trans($^b=>""):c}

Try it online!

trans is Raku's beefed-up version of Perl 5's transliteration operator tr.

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

Ruby, 29 bytes

->s,a{s.gsub(/./){a[_1]&&_1}}

Previous version which had flaws indicated by Deadcode:

->s,a{s.tr(s.tr(a,""),"")}

Try it online!

\$\endgroup\$
4
  • 2
    \$\begingroup\$ tr parses a like a regex character class; ranges (e.g. a-z) are parsed, and ^ at the beginning negates. So this answer is incorrect unless a can be quoted/escaped in such a way that no characters are treated specially. \$\endgroup\$
    – Deadcode
    Aug 13 at 20:40
  • \$\begingroup\$ Welcome to Code Golf! \$\endgroup\$ Aug 13 at 22:29
  • \$\begingroup\$ Nice. This can be shortened further, to 25 bytes: Attempt This Online! \$\endgroup\$
    – Deadcode
    Aug 13 at 23:44
  • \$\begingroup\$ Also, 27 byte version that works on the older Ruby version on TIO: Try it online! \$\endgroup\$
    – Deadcode
    Aug 13 at 23:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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