6
\$\begingroup\$

Challenge

Output the person on PPCG who has cast the most upvotes and the person who has cast the most downvotes.

Voters

You should only look at the people who have cast more than 1000 votes in total.

To find the person who upvotes the most, you should the find the person with the highest upvotes/total votes value. Similarly, the person who downvotes the most has the highest downvotes/total votes value.

Rules

Your output should be the two users' id (the number found in the URL of the user's page). You must put the upvoter first.

In the case of a tie, you must choose the user whose id is the lowest.

The Stack Exchange API is allowed, but entries in T-SQL using the Stack Exchange Data Explorer are disallowed.

Winning

The shortest code in bytes wins.

\$\endgroup\$
4
\$\begingroup\$

Java, 1106 bytes

import java.io.*;import java.util.regex.*;()->{String u="",y="",z="",s,t;float h=0,a,l=0,d;int i=-1,k=-1;Matcher m;Pattern p=null;while(i<=k){try(BufferedReader g=new BufferedReader(new InputStreamReader(new java.net.URL("https://codegolf.stackexchange.com/users"+(k==-1?"?tab=NewUsers&sort=creationdate":"/"+i+"/?tab=topactivity")).openStream()));){while((s=g.readLine())!=null){Integer v=0;if(k==-1&s.contains("users/")&!s.contains("profile-me")){m=p.compile("(?<=users/)(\\d+)").matcher(s);if(m.find()){k=v.parseInt(m.group());break;}}if(s.contains("User")){m=p.compile("(?<=User )(.*)(?= - P)").matcher(s);if(m.find())u=m.group();}if(s.contains("Cast")){m=p.compile("(?<=\\>\\().*(?=\\)\\<)").matcher(s);if(m.find()&&(v=v.parseInt(m.group().replace(",","")))>1000){while(1>0){s=g.readLine();m=p.compile("(\\d*,?\\d+)(?=\\<.*\\>)").matcher(s);if(m.find()){if((t=g.readLine()).contains("up")&&(a=0f+v.parseInt(m.group().replace(",",""))/v)>h){y=""+u;h=a;}if(t.contains("down")){if((d=0f+v.parseInt(m.group().replace(",",""))/v)>l){z=""+u;l=d;}break;}}}}}}}catch(Exception e){}}System.out.print(y+"\n"+z);}

As a full class:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.regex.*;

public class C {
    public static void main(String[] args) throws Exception {
        String u="", y="", z="", s, t;
        float h = 0, a, l = 0, d;
        int i = -1, k = -1;
        Matcher m;
        Pattern p = null;
        while (i < k) {
            // System.out.println(i);
            try (BufferedReader br = new BufferedReader(new InputStreamReader(new URL("https://codegolf.stackexchange.com/users" + (k == -1 ? "?tab=NewUsers&sort=creationdate" : "/" + i + "/?tab=topactivity")).openStream()));) {
                while ((s = br.readLine()) != null) {
                    Integer v = 0;
                    if (k == -1 && s.contains("users/") && !s.contains("profile-me")) {
                        m = p.compile("(?<=users/)(\\d+)").matcher(s);
                        if (m.find()) {
                            k = v.parseInt(m.group());
                            break;
                        }
                    }
                    if (s.contains("User")) {
                        m = p.compile("(?<=User )(.*)(?= - P)").matcher(s);
                        if (m.find())
                            u = m.group();
                    }
                    if (s.contains("Cast")) {
                        m = p.compile("(?<=\\>\\().*(?=\\)\\<)").matcher(s);
                        if (m.find() && (v = v.parseInt(m.group().replace(",",""))) > 1000) {
                            while (true) {
                                s = br.readLine();
                                m = p.compile("(\\d*,?\\d+)(?=\\<.*\\>)").matcher(s);
                                if (m.find()) {
                                    if ((t = br.readLine()).contains("up") && (a = 0f + v.parseInt(m.group().replace(",",""))/v)>h) {
                                        y = ""+u;
                                        h = a;
                                    }
                                    if (t.contains("down")) {
                                        if ((d=0f + v.parseInt(m.group().replace(",",""))/v)>l) {
                                            z = ""+u;
                                            l = d;
                                        }
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {}
        }
        System.out.print(y+"\n"+z);
    }
}

One word: Damn.

I mean, I've always wanted to do a challenge like this, but boy was it a doozy.

I didn't use the Stack Exchange API (don't know what it is, where to access it, or how to use it :P), so I basically had to do everything on my own; having the program crawl through each of the nearly 60,000 PPCG users' profile pages for the right data, looking up the HTML scripts and accounting for how the vote totals were laid out differently for different users1, and debugging (which was, perhaps unsurprisingly, a huge pain). And let me just say, this code could probably use some serious optimization. It takes well over two hours for the program to finish running on my machine, though most of the time is probably used opening the connections to the sites.

So here's a quick rundown of how it works:

  1. Create String variables to hold the upvotingest and downvotingest users on PPCG.
  2. Figure out the total number of active (and deleted/nonexistent) user accounts there are on the site. The program visits https://codegolf.stackexchange.com/users?tab=NewUsers&sort=creationdate and looks for the first user on the page, who happens to be the most recent addition to the community, and as such has the highest ID number. This ID number is set to be the total number of iterations for the loop.
  3. Start at ID 1 and visit https://codegolf.stackexchange.com/users/#, where the pound symbol represents the ID. Parse the HTML code (with regex͠,̰ nőͣ l͇̪e̐̃s̸͖̚͜͝s̡͍̲̫̋ͯ ) and collect the user's username, vote total, upvote total, and downvote total.
  4. Check to see if the upvotes to total votes ratio is larger than the current largest ratio, and set the current ratio to the new ratio if necessary. Do the same for the downvotes to total votes ratio, and also set the upvotingest and downvotingest user names when needed.
  5. Do steps 3 and 4 another (near) 60,000 times, incrementing the ID each time. (This takes care of the event when there is a tie.) If the user doesn't exist, either nothing matches or an Exception is thrown, and move on.
  6. Print the upvotingest and downvotingest users.

Yes, there are quite a few places where I can shave off a few bytes (especially on the strings where I use the contains method), but I'll save that for later... maybe.

If you decide to run this program, be prepared to wait a couple hours for it to finish execution.

Here's a screenshot of the output:

Output of the program

In case it's difficult to read, at the bottom it says...

gnat
Peter Taylor

...and the total time it took for the program to finish: a whopping two hours, forty-eight minutes, and twenty-three seconds!


1 The instance on which this occurred had to do with how recently the user voted. If the user voted at least once within the past week or month, two(?) additional stats showing how many times they voted over the past week or month appear on their activity page.

\$\endgroup\$
  • \$\begingroup\$ +1 This is a great solution. You can save yourself some bytes because the questiom asked for the user's id instead of their name :) \$\endgroup\$ – Beta Decay Sep 10 '16 at 8:19

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.