Regex (ECMAScript or better), 38 34 20 bytes
((.).*),?(.*)\1,?\3$
Uses ,
as the decimal separator. Returns its output as capture group \2
.
Try it on regex101 - ECMAScript (and can be switched to others)
Try it online! - ECMAScript
Attempt This Online! - PCRE2
Try it online! - .NET
It occurred to me after writing both of the below regexes that an entirely different approach would also work.
# Search for a "first half", which is repeated exactly following itself
# (except that there can be a decimal point anywhere in either half, which
# is not repeated), with the end of the string following that in turn.
((.).*) # \1 = part 1 of first half;
# \2 = first digit of this half (the return value)
# A decimal point may be here in the other half.
,? # Skip a decimal point, if there is one in this half. If there isn't
# one here in this half, there must be one here in the other half.
(.*) # \3 = part 2 of first half
# Match the other half, which is an exact repetition of the first half
# except that a decimal point can occur anywhere in either half and is
# not repeated.
\1 # Match part 1
,? # Skip a decimal point, if there is one here.
\3 # Match part 2
$ # Assert that we've reached the end of the string.
It even almost works in GNU ERE:
Attempt This Online! - but fails on the 5.0000000
case, because this engine really doesn't like to backtrack much. The strangest thing is that if REG_NOSUB
is enabled, it claims to match everything... is it lying? It's Schrödinger's match. (You can experiment with that by changing SHOW_MATCH
to 0
in the test harness.)
This 20 byte regex obsoletes the other two below, as it runs at the same speed or faster... but I'm not sure if I want to delete them, since they're interesting in their own right. And they would still be the only working solutions if the entire string could be littered with any number of decimal points.
Regex (.NET), 39 bytes
(?=((.),?)*$)(?<=(?(2)$)(?<-2>(\2),?)*)
Returns its output as capture group \3
.
Try it online!
This works (using .NET's Balancing Groups feature) by pushing a portion of the end of the string onto the group \2
capture stack, one digit at a time, and then scanning in reverse from the position it started from, popping off one digit at a time from the stack to check that they match. The last one popped off is then the return value, if it matches.
# No anchor - This regex will try starting its match at each
# character of the string until succeeding.
(?= # Positive atomic lookahead - Match the following, but then
# return to this position.
(
(.) # Push a digit onto the group \2 capture stack.
,? # Skip a decimal point if there is one here.
)* # Iterate the above as many times as possible, minimum zero
$ # Assert we've reached the end of the string.
)
(?<= # Lookbehind - evaluated right-to-left
# (read this from the bottom up)
(?(2)$) # Assert that if the group \2 stack is not empty, we're at the
# end of the string (which is impossible). Basically, assert
# that the group \2 stack is empty.
(?<-2> # Pop a capture from the group \2 stack and match:
(\2) # \3 = digit that matches with \2
,? # Skip a decimal point if there is one here.
)*
)
Try it on regex101 - putting this link down below here because regex101's .NET support is a bit buggy. With this example, it sees the capture groups in the wrong order, and thinks the return value is in group 1.
Regex (PCRE2), 48 bytes
(?*(.).*(.*+))((.),?(?=.*(?=\2$)(\5?+,?\4)))+\5$
Returns its output as capture group \1
.
Try it on regex101
Attempt This Online!
# No anchor - This regex will try starting its match at each
# character of the string until succeeding.
(?* # Non-atomic lookahead - Match the following, but then return
# to this position; if a non-match is subsequently found,
# backtrack to here and try other possible matches.
(.) # Capture the first digit in \1; this will be our return value.
.*(.*+) # Search for a \2 that is the last instance of the entire
# repeating pattern, using non-atomic lookahead.
)
( # Loop the following:
(.) # \4 = a digit
,? # Skip a decimal point if there is one here.
(?= # Positive atomic lookahead - Match the following, but then
# return to this position.
.*(?=\2$) # Skip to where \2 begins.
( # \5 = the following:
\5?+ # The previous value of \5, or nothing if this is the first
# iteration and \5 is unset.
,? # Skip a decimal point if there is one here.
\4 # Match a copy of the digit captured in \4.
)
)
)+ # Iterate as many times as possible (minimum one) to match the
# following assertion:
\5$ # Assert that the \5 we captured is identical to \2 and is
# located at the end of the string.
This uses a PCRE2 feature, non-atomic lookahead (?*
...)
. I'm pretty sure it's also possible to solve this without non-atomic lookahead, but I'll look into that later.
Note that the only reason this regex is so complicated is because of the presence of the decimal point. If not for that, the solution would be (?=(.+)\1$|.$).
(which is what I came up with before realizing the decimal point was in the way, when I still wanted to return the output as the match instead of a capture group), or what is used in ovs's Retina solution, ((.).*)\1$
.
But this is a pure regex solution, and can't do any substitutions before doing its matching work.
\$\large\textit{Full programs}\$
Ruby -n
, 27 bytes
~/((.).*),?(.*)\1,?\3$/;p$2
Try it online!
Perl -p
, 28 bytes
/((.).*),?(.*)\1,?\3$/;$_=$2
Try it online!
\$\large\textit{Anonymous functions}\$
Ruby, 33 bytes
->s{s=~/((.).*),?(.*)\1,?\3$/;$2}
Try it online!
JavaScript (ES6), 37 bytes
s=>s.match(/((.).*),?(.*)\1,?\3$/)[2]
Try it online!
Julia v0.4+, 38 bytes
s->match(r"((.).*),?(.*)\1,?\3$",s)[2]
Try it online!
R v4.1.0+, 48 bytes
\(s)gsub('.*?((.).*),?(.*)\\1,?\\3$','\\2',s,,1)
Attempt This Online!
Java 8, 49 bytes
s->s.replaceAll(".*?((.).*),?(.*)\\1,?\\3$","$2")
Try it online!
Java's split
method just doesn't work the right way to be useful for this.
PHP, 54 bytes
fn($s)=>preg_split('/(?=(.+),?(.*)\1,?\2$)/',$s)[1][0]
Try it online!
lambda s:re.split(r'(.+),?(.*)\1,?\2$',s)[1][0];import re
Try it online!
lambda s:__import__('re').split(r'(.+),?(.*)\1,?\2$',s)[1][0]
Try it online!
(If it must be a pure lambda.)
abb
, they are of the formatab[most of b]
. In the test case starting2588
, you have to pull the next digit froma
and not fromb
. I can't find a fixed pattern here. \$\endgroup\$b
is this case is2588588
,a
is empty. \$\endgroup\$a=12211
,b=2
probably. \$\endgroup\$a
is what's before the decimal, OK. In my head,a
being empty was shown as a0
before the decimal but that isn't right. So the 4th test case hasa
=00
andb
=526...210
. Now I can see the patterns. May I suggest adding more explanation to the test cases? \$\endgroup\$b
from input. And for example this input88.998899
offers multiple choices forb
, which are9
and8899
. \$\endgroup\$