Sort these James Bond ratings

32

1

Introduction

My grandpa is a fan of James Bond, but he is always unsure on how to rank his favourite actors. As such, he is always making lists, which is a lot of work. He asked me to produce a program that will make his life easier, but I do not have time for that, I have to work! So I will count on you guys.

Challenge

The challenge is simple. The input will consist of a list, in the following format:

<number> <space> <actor's name> <newline>

Your task is to sort them based on the number at the beginning of the line, starting from the last, and ending with the first. All numbers should be removed.

However, my grandpa sometimes makes mistakes. As such, you will need to validate the data. If one of the names on the list doesn't refer to one of the actors who played Bond, you need to discard it. In case of repetitions, repeats should be removed, and the name should maintain the lowest weight it was associated with (example #3).

There is no limit to how many lines there may be.

The output only needs to be a list of some sort, whether it is an array, a comma separated string, just values separated by spaces, or something else entirely, i.e.

Pierce Brosnan, Sean Connery, David Niven

A trailing newline or space is allowed.

Example Input and Output

Input:

1 Sean Connery

2 Emma Watson

5 Timothy Dalton

4 Roger Moore

3 Daniel Craig

Output:

Timothy Dalton, Roger Moore, Daniel Craig, Sean Connery

Input:

2 Timothy Dalton

4 George Lazenby

5 George Lazenby

3 Bob Simmons

Output:

George Lazenby, Bob Simmons, Timothy Dalton

Input:

3 Sean Connery

2 Pierce Brosnan

1 Sean Connery

Output:

Pierce Brosnan, Sean Connery

As this is a code golf, shortest code (in bytes) wins!

Appendix

List of actors who played the role of Bond:

  • Barry Nelson
  • Bob Simmons
  • Sean Connery
  • Roger Moore
  • David Niven
  • George Lazenby
  • Timothy Dalton
  • Pierce Brosnan
  • Daniel Craig

MKII

Posted 2016-02-11T12:52:08.923

Reputation: 353

3Welcome to PPCG, and nice challenge! Note that Sean Connery appears twice on your list. – Denham Coote – 2016-02-11T12:57:53.470

@DenhamCoote Fixed that and the mistake in the example output. – MKII – 2016-02-11T12:59:43.687

Does it matter which actor repetition we eliminate? – Denker – 2016-02-11T14:00:40.283

@FryAmTheEggman Oh, it would have to be the lowest value (2, in this case). – MKII – 2016-02-11T14:33:02.557

2Can we assume all possible actors will be identified by two words (first name and last name)? – Luis Mendo – 2016-02-11T17:11:36.280

17Emma Watson was great as James Bond. – Alex A. – 2016-02-11T18:11:07.827

@LuisMendo Yes, you can assume so. – MKII – 2016-02-11T22:15:21.540

What characters will appear in the names? Only letters? – Martin Ender – 2016-02-12T12:04:28.977

4hmm my answer is echo Sean Connery because everyone knows, there is only one bond – user902383 – 2016-02-12T14:04:27.713

You forgot Peter Sellers. Possibly Woody Allen. I think there were a few others too. (Hey, if you're going to count David Niven, you have to count the others in that movie.) – Darrel Hoffman – 2016-02-12T14:25:03.700

@DarrelHoffman Too late now, but true. – MKII – 2016-02-12T22:00:18.093

@MartinBüttner Only letters and a space (all names are 2 words separated by a space) – MKII – 2016-02-13T18:00:33.747

Answers

2

Pyth, 136 132 bytes

_{mtcd\ f}stcTdc"BarryNelson BobSimmons SeanConnery RogerMoore DavidNiven GeorgeLazenby TimothyDalton PierceBrosnan DanielCraig"dS.z

Try it here!

Explanation

_{mtcd\ f}stcTdc"BarryNelson BobSimmons ..."dS.z   # .z=list of all input lines
                                             S.z   # Sort input ascending
        f                                          # filter sorted lines with T being the current line
            cTd                                    # Split a line on spaces
          st                                       # Dicard the number and join first and last name
               c"BarryNelson BobSimmons ..."d      # Split the bond actor list on spaces...
         }                                         # only keep the lines which are in the actor list
   mtcd\                                           # remove the number from the filtered lines
_{                                                 # Remove duplicates from the mapping result and reverse the result

Denker

Posted 2016-02-11T12:52:08.923

Reputation: 6 639

Small flaw, the ordering is the wrong way around (it's supposed to go from last to first, while yours is first to last). – MKII – 2016-02-11T14:44:12.640

@MKII Guess I overread that part...Fixed it! – Denker – 2016-02-11T14:48:52.097

12

Retina, 201 197 191

\d+
$0$*1
G`^1+ (Barry Nelson|Bob Simmons|Sean Connery|Roger Moore|David Niven|George Lazenby|Timothy Dalton|Pierce Brosnan|Daniel Craig)$
+`\b((1+)\D*)¶(\2.+)
$3¶$1
+s`1+(\D+)¶(.*\1)
$2
1+ 

Try it online!

6 bytes saved thanks to Martin!

Whee, bubble sort with regex. Note that ten bytes are spent doing decimal to unary conversion at the beginning, if unary input is OK then that isn't needed. Also, if numbers can't be in people's names, then a couple more bytes can be saved by moving the line that removes non-Bond actors to the end and removing the 1+ (untested with \D version).

Explanation:

A Retina program is made up of several stages, so I will explain each stage separately.

Stage 1:

\d+
$0$*1

Replaces the numbers in the input with unary. This uses Retina's special replacement token: $* which repeats the character after a number of times equal to the preceding token's base 10 value.

Stage 2:

G`^1+ (Barry Nelson|Bob Simmons|Sean Connery|Roger Moore|David Niven|George Lazenby|Timothy Dalton|Pierce Brosnan|Daniel Craig)$

The stuff before a ` in a stage changes the mode being used. This turns on grep mode, which means that each line that doesn't match the regex is discarded. The anchors are necessary to prevent near matches from slipping by.

Stage 3:

+`\b((1+)\D*)¶(\2.+)
$3¶$1

This is the sorting stage. The + in the mode signifies that this stage should be repeated until the replacement makes no change when applied (i.e. we reach a fixed point). The regex finds a word boundry, followed by some number of 1s and then all the rest of the line up to the newline. Then, if the next line has more 1s than it, the regex will match and we swap the lines.

Stage 4:

+s`1+(\D+)¶(.*\1)
$2

This stage uses the + mode again, but also uses s to make the . meta-character also match newlines. This removes duplicate lines, by matching for exact duplicates after the 1s and capturing the stuff after the first duplicate to replace the whole match with it. This will work without needing to consider the order of tie breaking, because the names are already sorted appropriately, with the larger numbers above, therefore we will always keep the smaller values.

Stage 5:

1+ 

Really simple one here, everything is in order, except we have a bunch of 1s in front of our Bonds, so we replace them and the space after them with nothing.

FryAmTheEggman

Posted 2016-02-11T12:52:08.923

Reputation: 16 206

...Damn, this language impresses me more and more every day. Well done, Martin! – Fund Monica's Lawsuit – 2016-02-11T18:01:22.693

6

TSQL 426 bytes (includind data + input)

Golfed solution:

create table A(Name varchar(99))insert into A values('Barry Nelson'),('Bob Simmons'),('Sean Connery'),('Roger Moore'),('David Niven'),('George Lazenby'),('Timothy Dalton'),('Pierce Brosnan'),('Daniel Craig')declare @I as table (R int, N varchar(99))insert into @I values(3,'Sean Connery'),(2,'Pierce Brosnan'),(1,'Sean Connery')select N from(select N,min(R) R from @I where N in (select N from A) group by N) x order by R desc

Try it here

SQL excels (no pun intended) in this kind of task: relating sets, ordering, cutting off duplicates etc.

All you need is to create and populate a table of Actors like this:

create table Actor (Name varchar(99))
insert into Actor values
 ('Barry Nelson')
,('Bob Simmons')
,('Sean Connery')
,('Roger Moore')
,('David Niven')
,('George Lazenby')
,('Timothy Dalton')
,('Pierce Brosnan')
,('Daniel Craig')

Now if we use a table-variable as input we just need to get the intersection of both sets. Removing duplicates and ordering in SQL are really easy.

Example 1:

declare @Input as table (Rnk int, Name varchar(99))
insert into @Input values
 (1,'Sean Connery')
,(2,'Emma Watson')
,(5,'Timothy Dalton')
,(4,'Roger Moore')
,(3,'Daniel Craig')

select Name
from
(
    select Name, min(Rnk) as R
    from @Input
    where Name in (select Name from Actor)
    group by Name
) x
order by R desc

Example 2:

declare @Input as table (Rnk int, Name varchar(99))
insert into @Input values
 (2,'Timothy Dalton')
,(4,'George Lazenby')
,(5,'George Lazenby')
,(3,'Bob Simmons')

select Name
from
(
    select Name, min(Rnk) as R
    from @Input
    where Name in (select Name from Actor)
    group by Name
) x
order by R desc

The golfed version is just the full thing for example input 3

As a plus this SQL can work for older DBMS versions (even be rewrite to ANSI SQL) and run without problem in older computers than most languages.

jean

Posted 2016-02-11T12:52:08.923

Reputation: 177

Does it work with any number at the beginning of the line, or only single digits? – MKII – 2016-02-11T22:45:20.363

1

@MKII I used INT type so it ill accept anything in the range –2,147,483,648 to 2,147,483,647 also it ill accept that number of rows too =)

– jean – 2016-02-12T09:32:12.727

You don't need a subselect. You can just use order by min(R) desc with the inner select and remove the min(R) from the select. That should save 21 bytes. – raznagul – 2016-02-12T10:59:34.390

Also there are some unnecessary spaces in the golfed version. – raznagul – 2016-02-12T11:10:40.970

Using char instead of varchar will save another 6 bytes. – raznagul – 2016-02-12T11:26:01.633

@raznagul Yeah i can save a few bytes removing some white space but I'm just answered the question for the fun. For sure you can use a fancier language to achieve less bytes. Changing varchar to char for sure can save a few more bytes but I don't few that correct because varchar is the correct type for the set of variable length strings. Corretcness trumps anything! Finally no I cannot avoid the subselect because OP don't stated I can fetch the the actor's RANK and that can be a loophole. If OP can agree with a result set like Pierce Brosnan,2;Sean Connery,1 so it's OK – jean – 2016-02-12T12:00:42.980

5

Perl, 242 179 217 bytes

print reverse grep{/^(Barry Nelson|Bob Simmons|Sean Connery|Roger Moore|David Niven|George Lazenby|Timothy Dalton|Pierce Brosnan|Daniel Craig)$/&&!$s{$_}++}map{s/\d+ //;$_}sort{($a=~/(\d+)/)[0]<=>($b=~/(\d+)/)[0]}<>;

Nicer formatted version, with comments:

print
     # reverse ranking order
     reverse
     # filter entries...
     grep {
         # only actual bonds
         /^(Barry Nelson|Bob Simmons|Sean Connery|Roger Moore|David Niven|George Lazenby|Timothy Dalton|Pierce Brosnan|Daniel Craig)$/
         # only new bonds
         && !$s{$_}++
     } map {s/\d+ //;$_}         # remove leading digits+space
     # sort according to embedded numbers
     sort {($a=~/(\d+)/)[0] <=> ($b=~/(\d+)/)[0]}
     <>;                        # slurp input as list (list context)

Most of the size is the list of Bonds; I can't find a nice way of compressing that regex without allowing false positives.

David Morris

Posted 2016-02-11T12:52:08.923

Reputation: 241

Welcome to Programming Puzzles and Code Golf. Brilliant answer, +1. I was going to suggest that you add an explanation, but then I saw the edit. Maybe it's possible to compress the list of actors somehow... – wizzwizz4 – 2016-02-11T18:40:17.017

@wizzwizz4 I tried a few things to make that regex smaller, but the decoding always seems to cost more than you save---it's too sparse in what it accepts. – David Morris – 2016-02-11T21:52:44.790

Sadly, it needs to work with numbers, not only single digits. I am sorry, but I used the wrong term in the question. – MKII – 2016-02-11T22:46:05.323

@MKII aww, that costs me 38 bytes :( – David Morris – 2016-02-11T23:01:35.323

If there's an eval in Perl, and a built-in compression system... – wizzwizz4 – 2016-02-12T16:39:58.960

@wizzwizz4 what do you mean by a built-in compression system? I can't find a "generic" compression approach which doesn't increase the size of the bondlist. – David Morris – 2016-02-12T20:56:40.843

What about Base 95? (I forgot that Perl wasn't a golfing language :-/) But maybe Base 95 could compress it. Or a lower base, if possible. – wizzwizz4 – 2016-02-13T19:53:42.510

Replase reverse by descending sort: sort {($b=~/(\d+)/)[0]-($a=~/(\d+)/)[0]} – Mike – 2017-05-26T23:07:13.760

4

Python 2, 250 bytes:

lambda I:zip(*sorted({k:v for v,k in[x.split(' ',1)for x in I.split('\n')]if k in'Barry Nelson,Bob Simmons,Sean Connery,Roger Moore,David Niven,George Lazenby,Timothy Dalton,Pierce Brosnan,Daniel Craig'.split(',')}.items(),key=lambda t:-int(t[1])))[0]

Demo:

>>> L = ["Barry Nelson",
...     "Bob Simmons",
...     "Sean Connery",
...     "Roger Moore",
...     "David Niven",
...     "George Lazenby",
...     "Timothy Dalton",
...     "Pierce Brosnan",
...     "Daniel Craig"]

>>> I="""2 Timothy Dalton
... 4 George Lazenby
... 5 George Lazenby
... 3 Bob Simmons"""
>>> F(I,L)
('George Lazenby', 'Bob Simmons', 'Timothy Dalton')

>>> I = """1 Sean Connery
... 2 Emma Watson
... 5 Timothy Dalton
... 4 Roger Moore
... 3 Daniel Craig"""
>>> 
>>> F(I,L)
('Timothy Dalton', 'Roger Moore', 'Daniel Craig', 'Sean Connery')

Kasramvd

Posted 2016-02-11T12:52:08.923

Reputation: 161

Let us continue this discussion in chat.

– Rɪᴋᴇʀ – 2016-02-11T16:18:55.700

I just used dictionary comprehension in order to preserve the unique names, instead of set comprehension. – Kasramvd – 2016-02-11T16:33:49.827

10I'd pay to see Emma Watson as James Bond. – DJClayworth – 2016-02-11T16:47:02.893

Does it work with any number at the beginning of the line, or only single digits? – MKII – 2016-02-11T22:43:08.620

2

PowerShell v3+, 227 219 bytes

$a=$args-split"`n"|sort|%{$c,$b=-split$_;$b-join' '}|?{$_-in('Barry Nelson,Bob Simmons,Sean Connery,Roger Moore,David Niven,George Lazenby,Timothy Dalton,Pierce Brosnan,Daniel Craig'-split',')}|select -u
$a[$a.count..0]

121 bytes of that is just the list of actors ...

Takes input $args and -splits it on newlines with `n. Pipe that to sort, which will sort the entries numerically ascending, which is OK for now. We pipe those to a foreach loop |%{...}, each iteration take the entry, -split it on spaces, then -join the second half back together with a space (i.e., stripping the numbers off the beginning). Those (ascending) sorted names are now left on the pipeline. We pipe those through a where with ? that ensures they're -in the approved list of actors. Finally, we select only the -unique entries, which for duplicates will select the first one it encounters (i.e., the lowest-weighted one) and discard the rest. We store the resultant array of names into $a.

So, now we've got a sorted ascending list of actors. Since the challenge requires descending, we do an in-place reversal operation on $a by indexing from $a.count down to 0.

Example

PS C:\Tools\Scripts\golfing> .\sort-these-james-bond-ratings.ps1 "1 Sean Connery`n2 Emma Watson`n5 Daniel Craig`n4 Roger Moore`n3 Daniel Craig"
Roger Moore
Daniel Craig
Sean Connery

Edit -- don't need to use [array]::Reverse() when indexing will do

AdmBorkBork

Posted 2016-02-11T12:52:08.923

Reputation: 41 581

Can you not just use sort -Des rather than the array reversal? Granted, this could be broken in later versions of PowerShell, but I don't think it likely or a real issue ;) – VisualMelon – 2016-02-12T13:31:04.637

@VisualMelon I had considered that, but then the select -u would grab and retain the highest-valued ordering, rather than the lowest, so for my example the positions of Daniel Craig and Roger Moore would swap. My attempts to fix that resulted in longer code than the array reversal. – AdmBorkBork – 2016-02-12T14:00:05.213

ah, yes, that makes sense, I wasn't able to run it and missed that completely - it's a shame there is so much waste just for that reversal... – VisualMelon – 2016-02-12T15:23:35.883

2

Python 309 286 bytes

import sys
i='Barry Nelson.Bob Simmons.Sean Connery.Roger Moore.David Niven.George Lazenby.Timothy Dalton.Pierce Brosnan.Daniel Craig'.split('.')
print ', '.join(i.pop(i.index(x)) for x in zip(*sorted((x.strip().split(' ',1) for x in sys.stdin),None,lambda x:int(x[0]),1))[1] if x in i)

mtp

Posted 2016-02-11T12:52:08.923

Reputation: 61

Does it work with any number at the beginning of the line, or only single digits? – MKII – 2016-02-11T22:44:05.580

it didn't, it does now though :) – mtp – 2016-02-12T11:19:08.253

Looks like you can get rid of some extra spaces in here, for example after print or after a ) or ] – wnnmaw – 2016-02-12T14:24:17.277

1

JavaScript (ES6), 232 bytes

s=>s.split`
`.sort((a,b)=>(p=parseInt)(a)<p(b)).map(l=>l.replace(/\d+ /,"")).filter(l=>!p[l]&/^(Barry Nelson|Bob Simmons|Sean Connery|Roger Moore|David Niven|George Lazenby|Timothy Dalton|Pierce Brosnan|Daniel Craig)$/.test(p[l]=l))

Explanation

var solution =

s=>
  s.split`
`
  .sort((a,b)=>                 // sort the list by the number
    (p=parseInt)(a)<p(b)        // parseInt reads only the first number in a string
                                // the variable p also holds names that appeared in the
                                //     list previously
  )
  .map(l=>l.replace(/\d+ /,"")) // remove the number at the beginning of each line
  .filter(l=>
    !p[l]&                      // remove duplicates
    
    // Bond actor regex
    /^(Barry Nelson|Bob Simmons|Sean Connery|Roger Moore|David Niven|George Lazenby|Timothy Dalton|Pierce Brosnan|Daniel Craig)$/
    
    .test(p[l]=l)               // test for bondness and add the line to p
  )
<textarea id="input" rows="6" cols="40">1 Sean Connery
2 Emma Watson
5 Timothy Dalton
4 Roger Moore
3 Daniel Craig</textarea><br />
<button onclick="result.textContent=solution(input.value)">Go</button>
<pre id="result"></pre>

user81655

Posted 2016-02-11T12:52:08.923

Reputation: 10 181