Stories
Slash Boxes
Comments

Dev.SN ♥ developers

Journal by prospectacle

I've updated my voting system to allow preferential voting (the previous method uses approval voting). It gives a score to each candidate based on its rank. E.g. if there are 10 candidates, and you give a candidate 1st preference, it gets 10 points, second preference gets 9 points, etc.

It's therefore functionally equivalent to "range voting".

<?php

/* How to use:
    Put all emails in an array with values in $emails["text"] and $emails["user_id"];
    Put the list of valid candidates in the function valid_candidate();
    Put the check for user-authorisation in function valid_user();
    Enjoy.
*/

$emails_array = array(
    array("user_id"=>234, "text"=>"
            option1 = 1
            option3 = 2
            optionwhatever = 3
        "),
    // Duplicate user, will be handled correctly.
    array("user_id"=>234, "text"=>"
        Oops forgot one I like:
        Optionfour = 2
        // Did I mention:
        option1=1
        "
        ),
    array("user_id"=>1234,
        "text"=>"
        // I hate option1
        Option1 = 6
        option2 = 1
        ")
    );

function valid_user($user_id){return true;} // put user filter in here if necessary
function valid_candidate($name){ return true;} // is the name one of the candidates?

$number_of_options = 6;

// Process all emails
foreach ($emails_array as $email)
{

  // Is it a valid registered user?
  if (valid_user($email["user_id"]))
  {

    // Process each line of the email
    $email_lines = explode("\n", trim($email["text"]));
    foreach ($email_lines as $this_line)
    {
      // Does it have an '=' sign and only one = sign
      $equals_sign = strpos($this_line, "=");
      if ($equals_sign !== false)
      {
        $cleaned_up_line_text = trim($this_line, ";.!\t\n\r\0");
        $parts_of_line = explode("=", $cleaned_up_line_text);
        if (count($parts_of_line) == 2)
        {
            // Is it a valid candidate and rank?
            // Candidate is in lower case.
            $candidate = strtolower(trim($parts_of_line[0]));
            $rank = intval(trim($parts_of_line[1]));
            if (valid_candidate($candidate) && ($rank > 0) && ($rank <= $number_of_options))
            {
                // Get the score for this candidate.
                // The score is the number of options - how far it is ranked below 1.
                // e.g. a rank of 1 would give it a score of $number_of_options.
                // a rank of 2 gives it a score of $number_of_options -1.
                // See "range voting".
                $score = $number_of_options - ($rank-1);

                // Make sure this vote for this user hasn't already been cast
                if (!isset($user_votes[$email["user_id"]]) ||
                    !isset($user_votes[$email["user_id"]][$candidate]))
                {
                    // Remember this user has voted for this name already.
                    $user_votes[$email["user_id"]][$candidate] = true;

                    // Count the vote towards the total
                    if (!isset($candidate_votes[$candidate]))
                        $candidate_votes[$candidate]=$score;
                    else $candidate_votes[$candidate]+= $score;
                }
            } // end of check for valid vote values.
        } // of check for correctly formatted vote
      } // of check for equals sign
    } // End of for loop for lines of email
  } // of check for valid user.
} // end of for loop for all emails.

print "votes:<br>";
print_r($user_votes);
print "<br><Br>";
print "candiate_votes<Br>";
print_r($candidate_votes);

?>

 

Reply to: Refactoring?

    (Score: 2) by martyb on Monday March 31 2014, @08:07AM

    by martyb (76) on Monday March 31 2014, @08:07AM (#23514)

    Interesting discussion; thanks for the reply!

    Based on this and your prior posts, I'm starting to notice a pattern. It seems to me that there would be value in having separate functions for:

    1. Ballot extraction/filtering - A function that, given an arbitrary chunk of text, extracts and returns only that part which contains the vote.

    2. Ballot validation - A function that, given a filtered ballot and a set of flags, tests to see if the ballot is valid for the selected type of vote. Could be implemented as sub-functions selected by the passed flags. Flags could be OR'ed together and passed as a single argument. (I am not up-to-date on the different kinds of votes that could be taken, so please consider this as very rough) Flags might include:

      1. BALLOT_RANKING_DUPE_ALLOWED - when processing rank voting, multiple votes with the same rank are permitted
      2. BALLOT_RANKING_GAP_ALLOWED - when processing rank voting, votes need not be consecutive numbers
      3. BALLOT_RANKING_WRITEIN_ALLOWED - when processing rank voting, the voter may write-in one (or more) candidate(s).

      May also want to have an argument (e.g. ballot_choice_limit) to limit the number of votes on a ballot that will be tabulated. I.e. pick the top 3 (three) candidates out of the list of 7 (seven) candidates offered.

      The function would return a string containing: count of the errors found followed by the error text for each, separated by new-line characters. If there are no errors found, then the returned string would contain a zero, new-line char, and then a new-line delimited string containing the validated votes.

    3. Ballot vote extraction and scoring. Given that the vote has been validated, apply whatever scoring rules are desired. If there were 5 items on the ballot and the user rank voted them from 1 to 5, this might provide scoring (from top to bottom) of "5,4,3,2,1" or "10,8,6,4,2" or "25,16,9,4,1"... you get the idea.

    4. Vote tallying - This would be a separate pass through the validated and scored votes. Simply a bookkeeping step to find the winner.

    Am out of time, so will post this now. Am interested to see your feedback!

Post Comment

Edit Comment You are not logged in. You can log in now using the convenient form below, or Create an Account, or post as Anonymous Coward.

Public Terminal

Anonymous Coward [ Create an Account ]

Use the Preview Button! Check those URLs!


Logged-in users aren't forced to preview their comments. Create an Account!

Allowed HTML
<b|i|p|br|a|ol|ul|li|dl|dt|dd|em|strong|tt|blockquote|div|ecode|quote|sup|sub|abbr|sarc|sarcasm|user|spoiler|del|s|strike>

URLs
<URL:http://example.com/> will auto-link a URL

Important Stuff

  • Please try to keep posts on topic.
  • Try to reply to other people's comments instead of starting new threads.
  • Read other people's messages before posting your own to avoid simply duplicating what has already been said.
  • Use a clear subject that describes what your message is about.
  • Offtopic, Inflammatory, Inappropriate, Illegal, or Offensive comments might be moderated. (You can read everything, even moderated posts, by adjusting your threshold on the User Preferences Page)
  • If you want replies to your comments sent to you, consider logging in or creating an account.

If you are having a problem with accounts or comment posting, please yell for help.