I have this problem where I listen to so much music that I can never remember what songs are called. But even if I remember the lyrics, I won't always know the song's name. But lucky for me, there's a pretty neat formula to determine1 the title of a song just based on its lyrics.
I'll define the way a song title can be guessed as the line repeated in its lyrics the most times. If multiple lines are repeated the same number of times, choose the shortest of them. If multiple lyrics both occur the same number of times and are of equal length, you may choose any of them.
Given an input of a multiline string where each line is a lyric (you may take input as a list of lines), output the predicted title of the song, using the method outlined above.
For example, say this totally real song was stuck in my head and I couldn't remember its name:
Hello, world
Hello, world
I just got to say it, hello world
Goodbye, world
Goodbye, world
Goodbye
I can figure using this formula that the song's title is "Hello, world".
You may assume that the input string will contain no empty lines.
Test cases:
- Love Me Do → "Love, love me do"
- God Only Knows → "God only knows what I'd be without you"
- Never Gonna Give You Up → "Never gonna give you up"
- All Star → "Get the show on, get paid" OR "Get your game on, go play"
Links go to pastebins of the lyrics, with any " removed.
This is code-golf, so shortest code in bytes wins.
1 Accuracy not guaranteed. No refunds.
-
3\$\begingroup\$ It turns out you only get 1/4 correct rate on test cases :) \$\endgroup\$l4m2– l4m22023年01月09日 02:27:20 +00:00Commented Jan 9, 2023 at 2:27
-
2\$\begingroup\$ What if there is a tie in length as well as count? \$\endgroup\$chunes– chunes2023年01月09日 02:36:20 +00:00Commented Jan 9, 2023 at 2:36
-
\$\begingroup\$ @chunes In that case you may choose any of the tied lyrics. \$\endgroup\$noodle person– noodle person2023年01月09日 03:30:25 +00:00Commented Jan 9, 2023 at 3:30
-
\$\begingroup\$ @KevinCruijssen Depending on where you put your line breaks, I'd say that song should just be called "Hey now"... \$\endgroup\$Darrel Hoffman– Darrel Hoffman2023年01月09日 15:30:57 +00:00Commented Jan 9, 2023 at 15:30
20 Answers 20
05AB1E, 3 bytes
é.M
Input as a list of lines.
Try it online or verify all test cases.
Explanation:
é # Sort the (implicit) input-list by their length (shortest to longest)
.M # Pop and leave the (first/shortest) most frequent line
# (which is output implicitly as result)
-
1\$\begingroup\$ Defeating the creator :) \$\endgroup\$Rhaixer– Rhaixer2023年01月09日 05:52:00 +00:00Commented Jan 9, 2023 at 5:52
-
\$\begingroup\$ Cool that Vyxal sort of has a builtin for this. Nice solution \$\endgroup\$noodle person– noodle person2023年01月09日 15:37:20 +00:00Commented Jan 9, 2023 at 15:37
Excel (ms365), 74 bytes
Formula in B1:
=LET(x,TEXTSPLIT(A1,"
"),@SORTBY(x,MAP(x,LAMBDA(y,SUM(-(x=y)))),,LEN(x),))
TEXTSPLIT(A1," ")- Split input in an horizontal array called 'x';SORTBY(x,MAP(x,LAMBDA(y,SUM(-(x=y)))),,LEN(x),)- 1st Sort by count, then by length (both ascending by default);@- Use implicit intersection to only return the 1st element in the resulting array.
-
\$\begingroup\$ I think the result should only be
love me do. \$\endgroup\$jdt– jdt2023年01月09日 14:37:40 +00:00Commented Jan 9, 2023 at 14:37 -
\$\begingroup\$ The result should be "Love, love me do" \$\endgroup\$noodle person– noodle person2023年01月09日 15:32:36 +00:00Commented Jan 9, 2023 at 15:32
J, 28 17 bytes
{.@\:%@#&>+1#.=/~
-11 thanks to l4m2's idea. Also thanks to l4m2 for pointing out an improvement!
Consider:
Hello, world
Hello, world
I just got to say it, hello world
Goodbye, world
Goodbye, world
Goodbye
In J we take the input as a list of boxed strings:
┌────────────┬────────────┬─────────────────────────────────┬──────────────┬──────────────┬───────┐
│Hello, world│Hello, world│I just got to say it, hello world│Goodbye, world│Goodbye, world│Goodbye│
└────────────┴────────────┴─────────────────────────────────┴──────────────┴──────────────┴───────┘
=/~Create an equality table to see which lines equal other lines:1 1 0 0 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 0 0 11#.Sum the rows (resulting length is now equal to input length):2 2 1 2 2 1%@#&>+And add the reciprocals of each line length elementwise:2.083 2.083 1.0303 2.071 2.071 1.1429{.@\:Use that result to sort the input descending, and take the first:┌────────────┐ │Hello, world│ └────────────┘
-
\$\begingroup\$ J always seems very intimidating to me because of its syntax. I’d be interested to know how this solution works. \$\endgroup\$noodle person– noodle person2023年01月09日 03:38:05 +00:00Commented Jan 9, 2023 at 3:38
-
1\$\begingroup\$ @Jacob I updated the solution but added an explanation to the new one. \$\endgroup\$Jonah– Jonah2023年01月09日 04:40:00 +00:00Commented Jan 9, 2023 at 4:40
-
1\$\begingroup\$ Why do you need multiplying input length if len(line) is encoded after decimal point? \$\endgroup\$l4m2– l4m22023年01月09日 05:28:11 +00:00Commented Jan 9, 2023 at 5:28
-
\$\begingroup\$ tio.run/##7VPfa9RAEH7PX/… \$\endgroup\$l4m2– l4m22023年01月09日 05:44:10 +00:00Commented Jan 9, 2023 at 5:44
-
\$\begingroup\$ I removed some input line in tio link for lack of space, but you shouldn't \$\endgroup\$l4m2– l4m22023年01月09日 05:50:29 +00:00Commented Jan 9, 2023 at 5:50
JavaScript (Node.js), (削除) 65 (削除ここまで) 61 bytes
x=>x.sort(g=(a,b)=>a?(x.map(b=>b!=a?x:0)+a).length-g(b):0)[0]
Sort <different lines>*(<input length>-1)+<element length>
-
\$\begingroup\$ Nice solution! Didn’t think of doing it this way \$\endgroup\$noodle person– noodle person2023年01月09日 03:35:59 +00:00Commented Jan 9, 2023 at 3:35
PARI/GP, 38 bytes
s->vecsort(s,a->[[1|l<-s,a!=l],#a])[1]
Based on @l4m2's JavaScript answer.
Takes a list of lines.
PARI/GP, 46 bytes
s->vecsort(matreduce(s)~,a->#a[1]*I-a[2])[1,1]
Takes a list of lines.
Jelly, 6 bytes
Æṃ'LÞḢ
For some reason, Jelly's mode atom insists on vectorising over a list of lines, so we need '
Japt -g, 9 bytes
Input as an array of lines.
ü üÊÌcÌñÊ
ü üÊÌcÌñÊ :Implicit input of array
ü :Group & sort by value
ü :Group & sort by
Ê : Length
Ì :Last element
c :Flat map
Ì : Last element
ñ :Sort by
Ê : Length
:Implicit output of first element
-
2\$\begingroup\$ Love it! Beats my initial solution
ü üÊo mÎñÊ-g(10 bytes) which is pretty close to your 9-byte version. \$\endgroup\$noodle person– noodle person2023年01月09日 11:44:33 +00:00Commented Jan 9, 2023 at 11:44 -
\$\begingroup\$ Nice work, @Jacob; you seem to be getting to grips with Japt nicely. \$\endgroup\$Shaggy– Shaggy2023年01月09日 19:22:43 +00:00Commented Jan 9, 2023 at 19:22
-
\$\begingroup\$ Just came back to this, it seems that your 8-byte version is failing test cases. \$\endgroup\$noodle person– noodle person2023年01月12日 22:44:21 +00:00Commented Jan 12, 2023 at 22:44
-
\$\begingroup\$ Fails for "All Star" and "Hello world" \$\endgroup\$noodle person– noodle person2023年01月12日 22:46:48 +00:00Commented Jan 12, 2023 at 22:46
-
1\$\begingroup\$ Thanks, @Jacob. I actually realised that the other night down the pub but forgot to update the next morn'. Will do so tomorrow. \$\endgroup\$Shaggy– Shaggy2023年01月12日 23:36:28 +00:00Commented Jan 12, 2023 at 23:36
Vyxal, (削除) 11 (削除ここまで) 7 bytes
vOÞMİÞg
Explained
vOÞMİÞg
vO # [input.count(item) for item in input]
ÞM # indices of maximal items
İ # indexed into the input
Þg # shortest by length
Retina 0.8.2, 40 bytes
O`
O^$#`
$.&
O^$#`(.+¶)(?=(1円)*)
$#2
1G`
Try it online! Takes input as a list of newline-terminated strings. Explanation:
O`
Sort everything.
O^$#`
$.&
Sort order of length, then reverse the result. The reverse in the next sort will then bring the shortest line of equal occurrence count back to the front.
O^$#`(.+¶)(?=(1円)*)
$#2
Sort by count of subsequent occurrences. Only one of each of the most frequent lines will actually sort last, but that doesn't matter because we only need one of them. The sort is stable, so the most frequent lines remain sorted in descending order of length. The result is then reversed, bringing the shortest most frequent line to the start.
1G`
Keep only the first line.
Scala 3, 40 bytes
s=>s.maxBy(l=>(s.count(Set(l)),-l.size))
Returns a tuple from the comparison key, which are ordered lexicographically.
Factor, (削除) 62 61 (削除ここまで) 60 bytes
[ [ length ] sort-with dup histogram '[ _ at ] supremum-by ]
[ length ] sort-withSort by ascending length.dup histogram '[ _ at ] supremum-byFind the most frequent element that is encountered first.
PowerShell, 49 bytes
($input|group|sort C*,{-$_.Name.Length})[-1].Name
Input comes from the pipeline.
$input is an automatic variable that enumerates all input that is piped to a function.
The lines are passed to the cmdlet Group-Object which groups identical lines into GroupInfo objects with the properties Count and Name (the original line).
The objects are then sorted first by the Count property (how often a line was found), and then the negative (so both Count and Length can be sorted ascending) length of the line.
Then the last element of the sorted array [-1] is taken, and its Name property returned.
Output is implicit.
Will return "Woah, love me do" (same length as "Love, love me do") for the Beatles because of the ascending sorting.
Go, 140 bytes
import."strings"
func f(s string)(O string){m,M:=-1,make(map[string]int)
for _,l:=range Split(s,"\n"){if M[l]+=1;M[l]>m{O,m=l,M[l]}}
return}
Charcoal, 23 bytes
WS⊞υι≔Eυ⟦Noυι±Lι⟧θ§υ⌕θ⌈θ
Try it online! Link is to verbose version of code. Takes input as a list of newline-terminated strings. Explanation: Port of @mousetail's original Python answer.
WS⊞υι
Input the lines of the song.
≔Eυ⟦Noυι±Lι⟧θ
Get the count and negated length of each line.
§υ⌕θ⌈θ
Output the line that has the maximum value of that.
Pip -r, 15 bytes
@Y-(_Ng)AE#_SKg
The -r flag reads the program arguments from lines of stdin rather than command-line args. Attempt This Online!
Explanation
@Y-(_Ng)AE#_SKg
g ; Lines of stdin (due to -r flag)
SK ; Sort ascending according to this key function:
_Ng ; Number of occurrences of this line in the input list
-( ) ; Negated
AE ; Placed in a list with
#_ ; Length of the line
Y ; No-op, necessary for parsing
@ ; Get the first element of the sorted list
Arturo, 41 bytes
$[a][minimum arrange a=>size=>[size--a&]]
$[a][ ; a function taking an argument a
minimum ; get minimum element of
arrange a=>size ; input sorted by ascending length
=>[ ; by
size ; length of
-- ; set difference between
a ; input
& ; current line
] ; end minimum
] ; end function