11
\$\begingroup\$

Write the shortest possible program to remove all partial matched duplicates from a list and only retain the longest lines. Sort order does not matter.

For following inputs, the intent is to search for the string reminder followed by a number and retain the longest unique reminder + number lines.

Some rules

  • reminder is a hard-coded word, case-sensitive.
  • All lines contain exactly one match.
  • There is always a space between reminder and the number.
  • Only positive, whole numbers are used in the matches (not necessarily a contiguous set).
  • Lines with same length for the same reminder are possible. It does not matter what line is chosen.
  • Sort order of output does not matter.

Inputs

A short reminder 1
This is the longest reminder 1 available
This is the longest reminder 2 available, different length
A short reminder 2
A short reminder 3
This is the longest reminder 3 available, another different length
Another short reminder 3
A reminder -1 but really a short reminder 42
This is the longest reminder but really a short reminder 42.

Expected output

This is the longest reminder 1 available
This is the longest reminder 2 available, different length
This is the longest reminder 3 available, another different length
This is the longest reminder but really a short reminder 42.

Shortest code wins


Background: this stackoverflow question

\$\endgroup\$
41
  • 1
    \$\begingroup\$ What's a "partial matched duplicate"? \$\endgroup\$ – Zgarb Aug 14 '20 at 10:03
  • 2
    \$\begingroup\$ Nice! Now it turns into a fun challenge. (I assume you mean find the longest prefixes right?) \$\endgroup\$ – user96495 Aug 14 '20 at 10:09
  • 1
    \$\begingroup\$ In languages that do not have regex support, it might well be terser if the code can assume the [1..N] input style. I just wanted to confirm which you wanted, which is totally up to you. \$\endgroup\$ – Jonathan Allan Aug 14 '20 at 11:21
  • 1
    \$\begingroup\$ @JonathanAllan - I see. That's possible. I have adjusted This is the longest match -2 but really a short match 4. to This is the longest match but really a short match 4. That doesn't invalidates Jo Kings answer. \$\endgroup\$ – Lieven Keersmaekers Aug 14 '20 at 11:42
  • 3
    \$\begingroup\$ For future reference, it's best not to define any rules by an example but to define upfront, possibly with a worked example, and then to have tests that cover edge-cases. \$\endgroup\$ – Jonathan Allan Aug 14 '20 at 11:58

11 Answers 11

1
\$\begingroup\$

05AB1E, 17 bytes

é.¡#I¡εнžm(Ã}þ}€θ

05AB1E doesn't have any regex, so figuring out the best approach with test case containing negative numbers or multiples spaces between the string and the number (i.e. reminder 42) was a bit tricky, but still pretty happy with how short it turned out.

First input is a list of string lines, and the second input is the hard-coded string to match.

Try it online.

Explanation:

é             # Sort the (implicit) input-list of lines by length (shortest to longest)
 .¡           # Group the lines by:
   #          #  Split the string on spaces
    I¡        #  Split that list on the second input-word
      ε       #  Map each inner list of strings to:
       н      #   Only leave the first part of the list
        žm    #   Push builtin 9876543210
          (   #   Negate it to -9876543210
           Ã  #   Only keep those characters from the string
      }þ      #  After the map, only leave strings consisting of just digits;
              #  so this will remove empty strings and negative numbers from the list
  }€          # After the group by: map over each group:
    θ         #  And only leave the last (thus longest) line
              # (after which the resulting list of lines is output implicitly)

See this for a step-by-step on how the input is transformed into the output.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Amazing what people come up with \$\endgroup\$ – Lieven Keersmaekers Aug 20 '20 at 11:05
3
\$\begingroup\$

JavaScript (ES6),  85 82  79 bytes

a=>a.sort((a,b)=>-!b[a.length]).filter(s=>a[k=/reminder \d+/.exec(s)]^(a[k]=1))

Try it online!

How?

We first sort all strings from longest to shortest.

a.sort((a, b) =>
  -!b[a.length]  // 0 if 'b' is longer than 'a', -1 otherwise
)

We then filter the strings, keeping only the first occurrence of each reminder N key. The underlying object of the input array a[] is re-used to keep track of the keys that were already encountered.

.filter(s =>
  a[k = /reminder \d+/.exec(s)]
  ^
  (a[k] = 1)
)
\$\endgroup\$
0
3
\$\begingroup\$

Python 3.8, 134 132 bytes

import re
def f(l,d={}):
 for s in l:
  if len(d.get(n:=re.sub('.*reminder (\\d+).*','\\1',s))or'')<len(s):d[n]=s
 return d.values()

Try it online!

Tried recursive lambda approach but it's longer:

Python 3.8, 148 bytes

f=lambda l,d={}:l and(len(d.get(n:=re.sub('.*reminder (\\d+).*','\\1',s:=l.pop()))or'')<len(s)and d.update([(n,s)])or f(l,d))or d.values()
import re

Try it online!

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

Japt, 20 bytes

Takes input as an array of strings. Output is sorted by the matched number, in lexicographical order.

ü_f`ã„ %d+` gîñÊÌ

Try it (Header splits input string on newlines)

ü_f`... %d+` gîñÊÌ     :Implicit input of array
ü                       :Group and sort by
 _                      :Passing each through the following function
  f                     :  Match
   `... %d+`            :    Compressed string "reminder %d+", which translates to the RegEx /reminder \d+/g
             g          :  Get first match ('Cause matching returns an array)
              Ã         :End grouping
               ®        :Map
                ñ       :  Sort by
                 Ê      :    Length
                  Ì     :  Get last element
\$\endgroup\$
2
\$\begingroup\$

Retina 0.8.2, 68 bytes

O#$`
$.&
O#$`.*reminder (\d+).*
$1
.*(reminder \d+)(.*¶(.*\1\b))+
$3

Try it online! Explanation:

O#$`
$.&

Sort (in ascending order) numerically by length.

O#$`.*reminder (\d+).*
$1

Sort numerically by matched number, keeping lines with the same number sorted in length order.

.*(reminder \d+)(.*¶(.*\1\b))+
$3

Keep only the last line of consecutive lines with the same matched number.

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

Raku, 48 bytes

*.sort(-*.comb).unique(:as({~m/reminder\s\d+/}))

Try it online!

Sorts by longest first, then gets the unique elements by the reminder number.

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

V (vim), 39 bytes

ÎÄÒ0J
ú!
ò/reminder ä
y2e0dw+VGç0¾/d


Try it online!

Î                 # on every line (:%norm)
 Ä                # (M-D)uplicate the line
  Ò0              # (M-R)eplace all characters with '0'
    J             # (J)oin with the original line
                  # This turns each line into "0000000000 reminder 1"
ú!                # Reverse (M-z)ort (cursor ends up on first line)
ò                 # (M-r)ecursively (until error)
 /reminder ä      # goto /reminder \d/ (longest reminder X, here we find X)
y2e               # (y)ank (e)nd of (2) words: reminder \d+>
   0dw            # goto beginning and (d)elete (w)ord (the 0s)
      +           # goto start of next line
       VG         # highlight until end
         ç^R0¾/d  # in this highlighted region delete lines matching:
                  # (^R)egister 0 - contains the reminder (y)anked earlier
^O                # Jump back to prev cursor position and repeat until error
\$\endgroup\$
1
\$\begingroup\$

Perl 5 + -M5.10.0, 59 bytes

Uses the same approach as @Arbauld's answer, sorts the input by length then discards any sentences that contain a previously seen match.

say grep/reminder \d+/&&!${$&}++,sort{$b=~y///c-length$a}<>

Try it online!

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

Jelly, 28 bytes

No (direct) regex support in Jelly (it's only indirectly available by executing Python code).

ðœṣ“ǧƥ»;⁶¤Ḋe€ÞṪf)ØDĠị⁸LÞṪ$€

A monadic Link accepting a list of lists of characters which yields a list of lists of characters.

Try it online! (footer just splits at newlines, calls the Link and joins back by newlines.)

How?

ðœṣ“ǧƥ»;⁶¤Ḋe€ÞṪf)ØDĠị⁸LÞṪ$€ - Link: list of lists of characters, X
                  ØD         - digit characters
ð                )           - dyadic chain for each (line in X) - i.e. f(line, digit characters)
 œṣ                          -   split at substrings equal to:
          ¤                  -     nilad followed by link(s) as a nilad:
   “ǧƥ»                     -       compressed string "reminder"
         ⁶                   -       space character
        ;                    -       concatenate -> "reminder "
           Ḋ                 -   dequeue (leaving only strings to the right of a "match ")
              Þ              -   sort (these "parts") by
             €               -   for each (character, c, in part):
            e                -     (c) exists in (digit characters)?
               Ṫ             -   tail - giving us the single part starting with positive digit
                                        characters - N.B. a '0...' is always less, if present
                f            -   filter-keep (digit characters) - thus "42..." becomes "42"
                    Ġ        - group indices by value
                     ị       - index into:
                      ⁸      -   X - giving us a list of lists of lines with equal "number"
                           € - for each:
                          $  -   last two links as a monad:
                        Þ    -     sort by:
                       L     -       length
                         Ṫ   -     tail
\$\endgroup\$
1
\$\begingroup\$

Ruby, 53 bytes

->a{a.sort_by{|s|-s.size}.uniq{|s|s[/reminder \d+/]}}

Try it online!

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

Python 3, 100 bytes

lambda a:{max((j for j in a if'reminder '+i in j),key=len)for i in' '.join(a).split()if i.isdigit()}

Try it online!

Test cases borrowed from Noodle9.

\$\endgroup\$

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.