11
\$\begingroup\$

Golf the leaderboard of this question (id=111735). Your program should issue one HTTP or HTTPS request to StackExchange API, parse it and present to user in form similar to a typical Leaderboard Snippet

Sample output (for the question 47338 instead of this):

Leaderboard
    Author  Language    Size
1.  FryAmTheEggman  Pyth    19
2.  Peter Taylor    CJam    24
3.  Martin Ender    CJam    27
3.  Peter Taylor    GolfScript  27
5.  randomra    J   32
5.  Optimizer   CJam    32
7.  Timtech TI-Basic 83/84  40
8.  mollmerx    k   41
9.  Sp3000  ><> 45
10. Sherlock9   Ruby    47
11. Martin Ender    Mathematica 49
12. Alex A. Julia   57
13. Sp3000  Python 2    58
14. Zgarb   Haskell 68
15. Timtech GML 76
16. Jakube  Python 2    79
16. nimi    Haskell 79
18. coredump    Common Lisp 91
19. Jim Large   Ruby    92
Winners by Language
Language    User    Score
Python 2    Sp3000  58
CJam    Peter Taylor    24
Julia   Alex A. 57
TI-Basic 83/84  Timtech 40
><> Sp3000  45
Haskell Zgarb   68
GolfScript  Peter Taylor    27
Common Lisp coredump    91
Pyth    FryAmTheEggman  19
k   mollmerx    41
J   randomra    32
Ruby    Sherlock9   47
Mathematica Martin Ender    49
GML Timtech 76

Note the repeated ranks 3, 5 and 16. Maybe I'll even add a special non-competing answer just to force correct, non-simplified handing of the score duplicates.

The output should consist of:

  1. The line "Leaderboard"
  2. The line "\tAuthor\tLanguage\tSize"
  3. For each answer, tab-separated line of rank and a ., then answer author name, then language name, then score; in ascending order for score
  4. The line "Winners by Language"
  5. The line "Language\tUser\tScore"
  6. For each used language, tab-separated language name, author of the lower score answer and the score

In other words, something like as if one copies and pastes result of the leaderboard snippet of this question to a text file (without "\tLink" things). See also the reference implementation in Python.

Rules

  • No network access apart from one API request to api.stackexchange.com
  • No usage of API features or languages that appeared after the submission of this question.
  • First line of the answer post should be Leaderboard-compatible. If it breaks the leaderboard script attached to the question then answer is non-competing.
  • If newly added answer renders some existing answer broken then the author of the old answer should fix it (or it becomes non-competing).
  • Links to languages, striked out scores, etc. should be handleded.
  • Ranks should be handled like in the snippet (e.g. equal score => equal rank => gap in ranks).

Accepted answer is the answer with the lowest score after sufficient amount of inactivity (minimum 1 month).

Good idea

  • To test with question IDs 47338 (for duplicate score handling + striked out score handling) and 17005 (for links handling). This bumps the answer from Valid to Good and protects from breaks from later submissions.
  • To include output examples both for this and for overridden ID versions.

Not necessary

  • Handling of more than 100 answers (limit of API for single request)
  • Handling of comment overrides
  • Sorting of "Winners by Language" section
  • Discrimination of competing and broken answers

Leaderboard

var QUESTION_ID=111735,OVERRIDE_USER=7773;function answersUrl(e){return"https://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"https://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

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

Perl + Mojolicious, 468 456 469 504 bytes

Using Mojolicious library.

use v5.10;use ojo;while(@i=@{(g("http://api.stackexchange.com/2.2/questions/111735/answers?site=codegolf&filter=withbody&page=".++$p)->json//{})->{items}}){push@r,[$_->{owner}{display_name},(($h=x($_->{body})->at("h1,h2")||next)->at("a")||$h)->text=~/\s*([^,]+)\s*/,$h->text=~/(\d+)[^\d]*$/]for@i}$,="   ";say"Leaderboard
",Author,$l=Language,Size;say+(++$i,$s{@$_[2]}//=$i).".",@$_
for@r=sort{@$a[2]-@$b[2]}@r;%h=map{@$_[1],$_}reverse@r;say"Winners by $l
$l",User,Score;say$_,$h{$_}[0],$h{$_}[2]for keys%h

Ungolfed:

use v5.10;
use ojo;

my @r;
while (my @i = @{ (g("http://api.stackexchange.com/2.2/questions/111735/answers?site=codegolf&filter=withbody&page=" . ++$p)->json // {})->{items} }) {
    my $h = x($_->{body})->at("h1,h2") or next;
    push(@r, [$_->{owner}{display_name}, ($h->at("a") || $h)->text =~ /\s*([^,]+)\s*/, $h->text =~ /(\d+)[^\d]*$/]) for @i;
}

$, = "\t";
my %s;
say("Leaderboard\n", "Author", (my $l = "Language"), "Size");
say((++$i, $s{$_->[2]} //= $i) . ".", @$_) for @r = sort { $a->[2] <=> $b->[2] } @r;

my %h = map { $_->[1] => $_ } reverse(@r);
say("Winners by $l\n$l", "User", "Score");
say($_, $h{$_}[0], $h{$_}[2]) for keys(%h);
\$\endgroup\$
  • \$\begingroup\$ Can't locate ojo.pm in @INC -> Does it mean that it is not just "Perl", but "Perl+Mojolicious"? Does't using non-included-with-the-language libraries count as a standard loophole? \$\endgroup\$ – Vi. Mar 3 '17 at 21:22
  • \$\begingroup\$ If I patch question ID to 47338, I don't see correct handling of ties. Instead of having the sane rank entries disappear. \$\endgroup\$ – Vi. Mar 3 '17 at 21:23
  • \$\begingroup\$ Correction: same-score entries receive different rank. Although this does not render this answer invalid (yet), it's Not Good. \$\endgroup\$ – Vi. Mar 3 '17 at 22:23
  • \$\begingroup\$ @Vi. Fixed ties. \$\endgroup\$ – Denis Ibaev Mar 4 '17 at 12:31
  • 1
    \$\begingroup\$ Works. Probably the most serious remaining issue (and supposedly the source of the downvote) is the submission name. Maybe change it to "Perl + Mojolicious" to avoid competing with pure Perl solutions? Mojolicious seems to be oneliner-oriented (hence useful for golfing) library not installed in most Perl deployments, so can't be considered a part of the language. \$\endgroup\$ – Vi. Mar 4 '17 at 21:34
6
\$\begingroup\$

Python 3, 860 856 bytes

Golfed slightly, just to bootstrap the leaderboard and provide some template for other golfers:

import json,re,html as h,requests as r
p=print
u=h.unescape;a=[];n={}
for i in json.loads(r.get("https://api.stackexchange.com/2.2/questions/111735/answers?page=1&pagesize=100&site=codegolf&filter=!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe").text)["items"]:
    m=re.match(r'<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)',i["body"].splitlines()[0]);l=u(m.group(1));t=u(i["owner"]["display_name"]);s=m.group(2);a.append((t,l,s))
    if l not in n: n[l]=[]
    n[l].append((t,s))
p("Leaderboard\n\tAuthor\tLanguage\tSize")
z=0;y=None
for i in enumerate(sorted(a,key=lambda x:x[2])):
    if y==i[1][2]:z+=1
    else:z=0;y=i[1][2]
    p("%d.\t%s\t%s\t%s"%(i[0]+1-z,i[1][0],i[1][1],i[1][2]))
p("Winners by Language\nLanguage\tUser\tScore")
for i in n.keys():
    n[i].sort(key=lambda x:x[1])
    print("%s\t%s\t%s"%(i,n[i][0][0],n[i][0][1]))

Indented with tabs. The last print is deliberately not replaced by p to create a score tie with the Mathematica answer.

Ungolfed:

import json
import re
import html
import requests
url="https://api.stackexchange.com/2.2/questions/111735/answers?page=1&pagesize=100&site=codegolf&filter=!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe"
data=json.loads(requests.get(url).text)
answers=[]
languages={}
for i in data["items"]:
    header=i["body"].splitlines()[0]
    m=re.match(r'<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)', header)
    lang=html.unescape(m.group(1))
    author=html.unescape(i["owner"]["display_name"])
    score=m.group(2)
    answers.append((author, lang, score))
    if lang not in languages: languages[lang]=[]
    languages[lang].append((author, score))
answers.sort(key=lambda x:x[2])
print("Leaderboard")
print("\tAuthor\tLanguage\tSize")
rankadj=0
prevscore=None
for i in enumerate(answers):
    if prevscore == i[1][2]:
        rankadj+=1
    else:
        rankadj=0
        prevscore=i[1][2]
    print("%d.\t%s\t%s\t%s" % (i[0]+1-rankadj, i[1][0], i[1][1], i[1][2]))
print("Winners by Language")
print("Language\tUser\tScore")
for i in languages.keys():
    w=languages[i]
    w.sort(key=lambda x:x[1])
    print("%s\t%s\t%s" % (i, w[0][0], w[0][1]))

Note: it does not yet handle links correctly, so fails for, for example, question 17005.

\$\endgroup\$
  • 1
    \$\begingroup\$ Even if you answer it yourself, you have to golf an answer to a code-golf question. \$\endgroup\$ – NoOneIsHere Feb 28 '17 at 23:27
  • \$\begingroup\$ @NoOneIsHere, I'm not sure about "have to". Only if "if you anwer to a code-golf question and want to win". \$\endgroup\$ – Vi. Feb 28 '17 at 23:29
  • 3
    \$\begingroup\$ @Vi. Then include it in the question as a reference solution. This is an answer, which does not answer the (your own) question, which asks for golfed code, per code-golf. \$\endgroup\$ – NoOneIsHere Feb 28 '17 at 23:31
  • 1
    \$\begingroup\$ @Vi. nope. Either it's fully golfed or it's not allowed. If it can easily be golfed, then it's not an answer. I'd really say just to put it in the question as a reference solution. \$\endgroup\$ – Rɪᴋᴇʀ Feb 28 '17 at 23:40
  • 4
    \$\begingroup\$ @Vi. No, golf it to the best of your extend. \$\endgroup\$ – NoOneIsHere Mar 1 '17 at 1:04
1
\$\begingroup\$

Bash + JQ, 399 bytes

Note, this can almost certainly be golfed further, by optimizing the jq expression logic.

Golfed

curl api.stackexchange.com/2.2/questions/111735/answers?site=codegolf\&filter=withbody|zcat|jq -r '[.items[]|{o:.owner.display_name}+(.body|capture("^<h1>(?<l>.*?),.*?(?<b>\\d*)\\D*</h"))]|sort_by(.b|tonumber)|("Leaderboard\n\tAuthor\tLanguage\tSize",(keys[] as $i|.[$i]|"\($i+1).\t"+.o+"\t"+.l+"\t"+.b),"Winners by Language\nLanguage\tUser\tScore",(group_by(.l)|.[]|min_by(.b)|.l+"\t"+.o+"\t"+.b))'

Sample Output

Leaderboard
    Author  Language    Size
1.  zeppelin    Bash + JQ   399
2.  Tom JavaScript ES6  454
3.  Denis Ibaev Perl    456
4.  Vi. Python 3    860
Winners by Language
Language    User    Score
Bash + JQ   zeppelin    399
JavaScript ES6  Tom 454
Perl    Denis Ibaev 456
Python 3    Vi. 860
\$\endgroup\$
  • \$\begingroup\$ What version of jq is needed? I get error: capture is not defined \$\endgroup\$ – Vi. Mar 3 '17 at 21:17
  • \$\begingroup\$ I don't see repeated ranks when I patch the question ID to 47338. The submission may become invalid as they arrive (and I may later add a special non-competing pseudo-answer just to force correct handling of ranks). \$\endgroup\$ – Vi. Mar 3 '17 at 21:37
  • \$\begingroup\$ Martin Ender's CJAM answer has "CJam, <s>28</s> 27 bytes". It should be interpreted as 27, not 28, like in the official leaderboard snippet. Or Pyth's 19 vs 22 bytes. \$\endgroup\$ – Vi. Mar 3 '17 at 21:55
  • \$\begingroup\$ Note the point "* Links to languages, striked out scores, etc. should be handleded." in the challenge rules. \$\endgroup\$ – Vi. Mar 3 '17 at 21:57
  • \$\begingroup\$ @Vi - Should be all set now (see jqplay.org/s/LuZfAn2Pxr). Pyth answer is still 22 bytes, as it is simply not formatted correctly (the last byte count comes first). \$\endgroup\$ – zeppelin Mar 3 '17 at 22:30
1
\$\begingroup\$

Mathematica, 852 856 bytes

Uses the built-in JSONTools package. This isn't the kind of thing Mathematica's meant to be used for...so I used it!

p=Print;S=StringRiffle;L=Length;r=Range;out=Association@JSONTools`FromJSON[Import["http://api.stackexchange.com/2.2/questions/111735/answers?site=codegolf&filter=withbody"]];l={};i=Association/@(out["items"]);
(f=("body"/.i)[[#]];h=StringPosition[f,{"<h1>","</h1>"}];a="display_name"/.("owner"/.i)[[#]];s=StringSplit[StringTake[f,{h[[1]][[2]]+1,h[[2]][[1]]-1}],{",","<a>","</a>",">","<s>","</s>"," bytes","<strike>","</strike>"}];AppendTo[l,{a,s[[1]],ToExpression@s[[-1]]}])&/@r@L["body"/.i];l=SortBy[l,Last];o=r@L@l;If[l[[#]][[3]]==l[[#-1]][[3]],o[[#]]=o[[#-1]]]&/@r[2,L@l];
p@"Leaderboard"
p@"\tAuthor\tLanguage\tSize"
For[i=1,i<=L@l,i++,p[ToString@o[[i]]<>"."<>S[l[[i]][[#]]&/@r@3,"\t"]]]
l=SortBy[l,{#[[2]],#[[3]]}&];l=DeleteDuplicatesBy[l,#[[2]]&];
p@"Winners by Language"
p@"Language\tUser\tScore"
For[i=1,i<=L@l,i++,p[S[l[[i]][[#]]&/@{2,1,3},"\t"]]]
\$\endgroup\$
  • \$\begingroup\$ How do I test it? I get ReplaceAll::reps: <content of the downloaded Mathematica answer> is neither a list of replacement rules nor a valid dispatch table, and so cannot be used for replacing., after which the prints are {$Failed, $Failed, $Failed, $Failed, $Failed, bytes, $Failed, $Failed}}. \$\endgroup\$ – Vi. Mar 7 '17 at 9:09
  • \$\begingroup\$ Maybe the solution is broken by its own source code (which obviously contains <h1>)? \$\endgroup\$ – Vi. Mar 7 '17 at 9:16
  • \$\begingroup\$ @Vi. That's strange, it's working for me. Which question did you test it on? I don't think it's the source code breaking it, because the StackExchange API uses HTML escapes for the < and > automatically. \$\endgroup\$ – numbermaniac Mar 8 '17 at 7:30
  • \$\begingroup\$ This one, 111735. But I substituted the URL with /tmp/q.json where there is a pre-downloaded JSON reply. \$\endgroup\$ – Vi. Mar 8 '17 at 10:01
  • \$\begingroup\$ Output for 47338: paste.debian.net/918716 \$\endgroup\$ – Vi. Mar 8 '17 at 10:06

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.