Shorten the Java Package

Briefing

Given a Fully Qualified Java Class/ Package name, you must shorten it as such:

Each part of the dot separated package will be shortened to it's first letter, apart from the last section and the class (if it exists).

package names will be all lower case, and the class (if it exists) will start with a capital letter and be UpperCamelCase. packages come in the form of:

foo.bar.foo


and

foo.bar.foo.Class


Examples

(No Class)
Input com.stackoverflow.main
Output c.s.main

(Class)
Output c.g.parser.Gson

(Class)
Output c.g.l.package.TestClass


Rules

• Shortest code in bytes wins
• Standard loopholes apply
• Does the class name always come last? – Shaggy May 5 '17 at 10:46
• @Shaggy yes it does – Shaun Wild May 5 '17 at 10:59
• Related – Peter Taylor May 5 '17 at 11:11
• Would fantastic.foo.func.bar become f.f.f.bar or is it guaranteed that 2 packages can't start with the same letter? – Emigna May 5 '17 at 11:11
• f.f.f.bar would be correct. – Shaun Wild May 5 '17 at 12:36

Retina, 17 bytes

\B\w+(\.[a-z])
$1  Try it online! Explanation \B # Start from a position that isn't a word boundary. This ensures that # the first letter of the package name is skipped. \w+ # Match one or more word characters. This is the remainder of the # package name which we want to remove. ( # Capture the next part in group 1, because we want to keep it... \.[a-z] # Match a period and a lower-case letter. This ensures that we # don't match the package that precedes the class, or the package or # class at the end of the input. )  This is replaced with $1, which is the period and lower case letter which shouldn't be removed.

• I wanted to try my first Retina answer but the Retina god Martin showed up. Way to go, Me! – Matthew Roh May 6 '17 at 0:33

JavaScript (ES6), 68 53 bytes

s=>s.split..map((x,y,z)=>z[y+1]>"["?x[0]:x).join.

• 15 bytes saved thanks to Arnauld.

See my other solution here.

Try it

f=
s=>s.split..map((x,y,z)=>z[y+1]>"["?x[0]:x).join.
console.log(f("com.stackoverflow.main"))
console.log(f("c.g.parser.Gson"))
console.log(f("com.google.longer.package.TestClass"))
<input id=i><pre id=o>

• Could it be simplified to s=>s.split..map((x,y,z)=>z[y+1]>"["?x[0]:x).join. ? – Arnauld May 5 '17 at 14:27
• Seems like it can, thanks, @Arnauld. – Shaggy May 5 '17 at 14:33
• Innovative solution but only 2 upvotes. But I will upvote this! – Arjun May 6 '17 at 14:09

Mathematica, 75 bytes

#[[;;-3]]~StringTake~1~Join~#[[-2;;]]~StringRiffle~"."&[#~StringSplit~"."]&


Anonymous function. Takes a string as input and returns a string as output.

• JS scoring lower than, Mathematica?! That can't be right - I've done something wrong, haven't I? – Shaggy May 5 '17 at 11:26
• @Shaggy Java 7 scoring lower than JS and Mathematica?! That can't be right - I've done something wrong, haven't I? (Couldn't resist it. ;) All credit goes to MartinEnder's Retina's port btw). – Kevin Cruijssen May 5 '17 at 11:38
• @KevinCruijssen, thanks for drawing my attention to Martin's answer - I've now fixed the problem of Java out-scoring JS! :D – Shaggy May 5 '17 at 11:48

Japt, 30 27 25 bytes

¡Y>Zl -('[>ZgJ)-2?X:Xg}'.


Try it online!

• Nice one! I think you can save two bytes with ('[>ZgJ) – ETHproductions May 5 '17 at 11:44
• I just realised that as well ;-) – Luke May 5 '17 at 11:45

Python 2, 76 73 bytes

q=input().split('.')
i=~(q[-1]<'_')
q[:i]=zip(*q[:i])[0]
print'.'.join(q)


Python 2, 88 81 bytes

f=lambda s,d=".":s.count(d)>(s.split(d)[-1]<"[")and s[0]+d+f(s[s.find(d)+1:])or s


Try it online!

String c(String s){return s.replaceAll("\\B\\w+(\\.[a-z])","$1");}  Port from @MartinEnder's amazing Retina answer. Try it here. JavaScript (ES6), 36 bytes Another port of Martin's Retina answer. See my other solution here. s=>s.replace(/\B\w+(\.[a-z])/g,"$1")


f=
s=>s.replace(/\B\w+(\.[a-z])/g,"\$1")
console.log(f("com.stackoverflow.main"))
console.log(f("c.g.parser.Gson"))
console.log(f("com.google.longer.package.TestClass"))
<input id=i><pre id=o>

• A good solution but no upvotes. It deserves more than a 0 score. I will turn it into 1! :) – Arjun May 6 '17 at 14:10

V, 9 bytes

Íì“ƒ…®õÀ!


Try it online!

Hexdump:

00000000: cdec 9383 85ae f5c0 21                   ........!


This is a wonderful example of V's signature regex compression.

Explanation:

Í          " Remove every match on every line:
ì         " A lower case letter
“ …      "   *ONLY MATCH THIS PART:*
ƒ       "   As few characters as possible
®    " Followed by a dot
õÀ! " Not followed by an uppercase letter


Python 2, 10897 89 bytes

i=input().split(".")
for x in range(len(i)+~(i[-1][0]<"[")):i[x]=i[x][0]
print".".join(i)


Try it online!

-8 with many thanks to @ovs for the tip

• for x in range(len(i)+~(i[-1][0]<"[")):i[x]=i[x][0] for -8 – ovs May 5 '17 at 15:04

sed, 57 22 bytes

I expected sed solution to be a little shorter than this...

Edit:

The shorter solution uses regex from Martin Ender's answer.

21 bytes sourcecode + 1 byte for -r flag (or -E flag for BSD sed).

s|\B\w+(\.[a-z])|\1|g

• Can't you also use the substitution from my Retina answer? s|\B\w+(\.[a-z])|\1|g? – Martin Ender May 5 '17 at 18:44
• @MartinEnder I spent quite some time trying to come up with one-liner without looking at anyone's answers first but failed. I don't think there is a shorter way to do it, so I will use yours. Thank you! – Maxim Mikhaylov May 5 '17 at 19:03

f s|[(a:t,p:x:r)]<-lex s=a:concat[t|x<'a']++p:f(x:r)|1<3=s

Try it online! Usage: f "some.string".
lex parses a string as Haskell tokens, so lex "some.string" returns [("some",".string")]. f recurses over the tokens in the string and always appends the first char a of the current token, but the rest t of the token only if the remaining string after the colon p starts with an uppercase char, that is x<'a'. If the pattern match failed, we have reached the last token and simply return s.