11
\$\begingroup\$

This question already has an answer here:

Have you ever found a good challenge to answer, answered it and then found out somebody posted a solution with a better score in the same language?

Challenge

Write a program/function that takes a PPCG question ID and outputs a leaderboard.

A leaderboard consists of the rank, the language, the score, the author and the URL of each answer. This can be output as a newline delimited string, a list of strings, a "dictionary", etc..

  • Rank
    • The rank of an answer is its position in the score hierarchy (lowest first).
    • Answers with shared scores must have the same rank.
  • Language
    • The name of the language is always between the last # on the first line and the first ,.
    • It may be a link to documentation, which must be handled by taking the user-facing text.
  • Score
    • The score of a answer is the last number on the first line.
    • It may be a decimal number.
  • Author
    • The author of an answer is the display name of the user that posted it.
  • URL

Specifications

  • Standard I/O rules apply.
  • Standard loopholes are forbidden.
  • This challenge is not about finding the shortest approach in all languages, rather, it is about finding the shortest approach in each language.
  • Your code will be scored in bytes, usually in the encoding UTF-8, unless specified otherwise.
  • Explanations, even for "practical" languages, are encouraged.

Test cases

Note that these show example output formats which need not be strictly followed. These examples were taken at the time of sandboxing, they might be outdated.

Input: 28821

1. PCRE flavor, score 40 by jimmy23013 (https://codegolf.stackexchange.com/a/31863)

Input: 92944

1. Jelly, score 12 by Dennis (https://codegolf.stackexchange.com/a/92958)
2. Befunge-98, score 38 by Hactar (https://codegolf.stackexchange.com/a/98334)
3. ><>, score 41 by Sp3000 (https://codegolf.stackexchange.com/a/92980)
4. TovTovTov, score 810147050 by Yaniv (https://codegolf.stackexchange.com/a/93048)

Input: 47604*

1. Pyth, score 28.08 by Jakube (https://codegolf.stackexchange.com/a/47642)
2. APL, score 39.6 by jimmy23013 (https://codegolf.stackexchange.com/a/47606)

Input: 133793

1. Lua, score 9.06 by TehPers (https://codegolf.stackexchange.com/a/135775)
2. C# (.NET Core), score 10.20 by Kamil Drakari (https://codegolf.stackexchange.com/a/135764)
3. Cubically, score 86.98 by Kamil Drakari (https://codegolf.stackexchange.com/a/137695)

*Note that this challenge has 3 answers, however the GolfScript answer doesn't have a comma in the header which makes it undefined behaviour.

In a few better formats:

28821, 92944, 47604, 133793

28821 92944 47604 133793

28821
92944
47604
133793

This challenge was sandboxed.

\$\endgroup\$

marked as duplicate by pppery, pajonk, Toto, Wheat Wizard, Mego code-golf Aug 9 '17 at 16:42

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • \$\begingroup\$ Is using libraries allowed? For example the HTML Agility Pack for C#. \$\endgroup\$ – Ian H. Aug 7 '17 at 7:34
  • \$\begingroup\$ @IanH. That is the same as always, you can use libraries but make sure to specify that in the header. Wouldn't want it to mislead people on leaderboards. ;) \$\endgroup\$ – totallyhuman Aug 7 '17 at 7:43
  • \$\begingroup\$ Does the PPCG default leader board count as a language? If yes does it count as 1 byte? :P \$\endgroup\$ – TheLethalCoder Aug 7 '17 at 8:58
  • \$\begingroup\$ @TheLethalCoder You can golf the stack snippet and post it as a JS answer (no, it does not count as 1 byte :P). However, I'm sure you guys can come up with more interesting answers. \$\endgroup\$ – totallyhuman Aug 7 '17 at 13:03
  • 3
    \$\begingroup\$ I like your title :P \$\endgroup\$ – Daniel Aug 7 '17 at 14:31
3
\$\begingroup\$

C# (.NET Core), 1059 1049 1033 998 973 bytes

Saved 53 bytes thanks to @TheLethalCoder.
Saved 25 bytes thanks to @Mr. Xcoder.

using HtmlAgilityPack;namespace System.Linq{n=>{var u="http://codegolf.stackexchange.com/";var l=new Collections.Generic.List<Tuple<string,float,string,string>>();foreach(var x in new HtmlWeb().Load(u+"questions/"+n).GetElementbyId("answers").Descendants("div").Where(p=>(p.Attributes["id"]?.Value??"").Contains("answer-"))){var b=x.Descendants().First(p=>(p.Attributes["class"]?.Value)=="post-text");var h=b.Descendants().ElementAt(1).InnerText;l.Add(new Tuple<string,float,string,string>(h.Split(',',' ')[0],float.Parse(h.Split(' ').Reverse().First(p=>float.TryParse(p.Replace('.',','),out var y)).Replace('.', ',')),b.ParentNode.Descendants("td").Last(p=>(p.Attributes["class"]?.Value)=="post-signature").Descendants("div").Reverse().ElementAt(1).Descendants("a").First().InnerText,u+"a/"+x.Attributes["id"].Value.Split('-')[1]));}var s=new string[l.Count];for(var i=0;i<l.Count;){var m=l[i];s[i]=++i+$". {m.Item1}, score {m.Item2} by {m.Item3} ({m.Item4})";}return s;}}

Uses the HTMLAgilityPack.

There are probably better solutions to this using the StackOverflow API, but this is a raw HTML solution.


Ungolfed code: (Unchanged for readability & reference)

n =>
{
    // Retrieve the HTML of the question page with ID n
    var doc = new HtmlWeb().Load("https://codegolf.stackexchange.com/questions/" + n);
    // Get all the answers
    var answers = doc.GetElementbyId("answers").Descendants("div").Where(d => (d.Attributes["id"]?.Value ?? "").Contains("answer-"));

    // Format: Language, Score, Author, URL
    var l = new Collections.Generic.List<Tuple<string, float, string, string>>();

    // Loop over every answer element
    foreach (var a in answers)
    {
        // Get the content of the answer post
        var b = a.Descendants().First(d => (d.Attributes["class"]?.Value ?? "") == "post-text");
        // Get the header
        var h = b.Descendants().ElementAt(1);
        // Dummy variable for the TryParse
        float y;
        // Create a new Tuple
        var v = new Tuple<string, float, string, string>(
            h.InnerText.Split(',', ' ')[0], // The language
            float.Parse(h.InnerText.Split(' ').Reverse().First(x => float.TryParse(x.Replace('.',','), out y)).Replace('.', ',')), // The score of the answer
            b.ParentNode.Descendants("td").Last(d => (d.Attributes["class"]?.Value ?? "") == "post-signature").Descendants("div").Reverse().ElementAt(1).Descendants("a").First().InnerText, // The author
            "https://codegolf.stackexchange.com/a/" + a.Attributes["id"].Value.Split('-')[1] // The url to the answer
            );
        l.Add(v); // Add the tuple to the collection
    }
    var s = new string[l.Count]; // An array to save the leadboard content
    for (var i = 0; i < l.Count;i++)
    {
        var m = l[i]; // The current element
        s[i] = $"{i+1}. {m.Item1}, score {m.Item2} by {m.Item3} ({m.Item4})"; // Format the data
    }
    return s;
}
\$\endgroup\$
  • \$\begingroup\$ Place your code into namespace System.Linq then move so you can remove the using. Also could you add a formatted version so it's easier to read all that code? :) \$\endgroup\$ – TheLethalCoder Aug 7 '17 at 10:02
  • \$\begingroup\$ As far i can see you only use d and a in one place so you could probably inline them inside the foreach making it foreach(var x in new HtmlWeb().Load("https://codegolf.stackexchange.com/questions/"+n).GetElementbyId("answers").Descendants("div").Where(p=>(p.Attributes["id"]?.Value??"").Contains("answer-"))) also if you're using C# 7 you should be able to remove float y; and change the TryParse to float.TryParse(p.Replace('.',','),out var y) \$\endgroup\$ – grabthefish Aug 7 '17 at 10:23
  • \$\begingroup\$ you could probably also save a lot of bytes if you use the new C# 7 tuple declaration \$\endgroup\$ – grabthefish Aug 7 '17 at 10:29
  • \$\begingroup\$ You can also inline v into the Add() call. Set h to .InnerText to save the repeated calls to it. Pre increment i at {i+1}->{++i}. Then move it out of the string ++i + $". {m.... \$\endgroup\$ – TheLethalCoder Aug 7 '17 at 10:54
  • 3
    \$\begingroup\$ Can someone explain why they downvoted this answer? \$\endgroup\$ – Ian H. Aug 8 '17 at 6:31
2
\$\begingroup\$

Python 2 + requests + BeautifulSoup + re, 547 519 512 bytes

from requests import*
from bs4 import BeautifulSoup as B
import re
w,h='.stackexchange.com/','http://'
def D(i):l=B(i['body'].split('\n')[0],'html.parser').text;return[l.split(',')[0],float(re.findall(r'\d*\.\d+|\d+',l)[-1]),i['owner']['display_name'],'%scodegolf%sa/'%(h,w)+`i['answer_id']`]
d=sorted([D(i)for i in get('%sapi%squestions/%d/answers?site=codegolf&filter=withbody'%(h,w,input())).json()['items']],key=lambda x:x[1])
h=r=0
for x in d:r+=x[1]>h;h=x[1];print'%d. %s, score %f by %s (%s)'%tuple([r]+x)

Output for this question (137878):

1. Python 2 + requests + BeautifulSoup + re, score 512.000000 by Dopapp (http://codegolf.stackexchange.com/a/138043)
2. C# (.NET Core), score 973.000000 by Ian H. (http://codegolf.stackexchange.com/a/137933)

Ungolfed:

from requests import*
from bs4 import BeautifulSoup
import re

# [score, owner, lang, link]
def data(item): # extracts data from each answer
    owner = item['owner']['display_name']
    link = 'http://codegolf.stackexchange.com/a/'+`item['answer_id']`
    line1 = BeautifulSoup(item['body'].split('\n')[0], 'html.parser').text # first line of the answer
    lang = line1.split(',')[0]
    score = float(re.findall(r'\d*\.\d+|\d+', line1)[-1])
    return [score, owner, lang, link]

# datas :P
datas = [data(item)for item in get('http://api.stackexchange.com/questions/%d/answers?site=codegolf&filter=withbody'%input()).json()['items']]

sortedDatas = sorted(datas, key=lambda x: x[0]) # sort the data by the score

highestScore=rank=0 # This is for doing the ranking in case there are identical scores
for d in sortedDatas:
    if d[0] > highestScore:
        highestScore = d[0]
        rank += 1
    print '%d. %s, score %f by %s (%s)' % (rank, d[2], d[0], d[1], d[3]) # format the line
\$\endgroup\$
  • \$\begingroup\$ You can remove some bytes with import BeautifulSoup as b, no? \$\endgroup\$ – Dave Aug 9 '17 at 14:25
  • \$\begingroup\$ @pizzakingme, yes good idea! \$\endgroup\$ – Daniel Aug 9 '17 at 14:40

Not the answer you're looking for? Browse other questions tagged or ask your own question.