Scripting-Challenge: Fussballergebnisse in Ligatabelle umwandeln

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
tobo
Beiträge: 1270
Registriert: 10.12.2008 10:51:41

Re: Scripting-Challenge: Fussballergebnisse in Ligatabelle umwandeln

Beitrag von tobo » 16.01.2022 19:08:23

Eine mögliche Lösung in Python:

Code: Alles auswählen

#!/usr/bin/env python3

import sys

if len(sys.argv) != 2:
    sys.exit()
FN=sys.argv[1]
d={}   #values={N:[w,d,l,gp,gm,ge,p]}

with open(FN) as f:
    for game in f:
        team1,goals1,goals2,team2=game.rstrip().split(',')
        for team in team1,team2:
            if team not in d:
                d[team]=[0,0,0,0,0,0,0]
        d[team1][3]+=int(goals1); d[team2][4]+=int(goals1)
        d[team1][4]+=int(goals2); d[team2][3]+=int(goals2)
        d[team1][5]=d[team1][3]-d[team1][4]; d[team2][5]=d[team2][3]-d[team2][4]
        if goals1==goals2:
            d[team1][1]+=1; d[team2][1]+=1
            d[team1][6]+=1; d[team2][6]+=1
        elif goals1>goals2:
            d[team1][0]+=1; d[team2][2]+=1
            d[team1][6]+=3
        else:
            d[team1][2]+=1; d[team2][0]+=1
            d[team2][6]+=3

out=sorted(d.items(), key=lambda t:(-t[1][6],-t[1][5],-t[1][0],t[0]))
print("                     Name  #  w  d  l  +  -   =  P")
print("--------------------------------------------------")
for n,i in enumerate(out,1):
    print("{:>25s} {:2d} {:2d} {:2d} {:2d} {:2d} {:2d} {:3d} {:2d}".format(i[0],n,*i[1]))

Code: Alles auswählen

$ ./t.py scripting_contest-game_results_utf8.csv 
                     Name  #  w  d  l  +  -   =  P
--------------------------------------------------
                FC Zürich  1 16 10 10 70 59  11 58
  Grasshopper Club Zürich  2 17  7 12 79 70   9 58
           BSC Young Boys  3 16 10 10 66 58   8 58
        FC Lausanne-Sport  4 16  6 14 58 63  -5 54
       FC St. Gallen 1879  5 14  9 13 59 58   1 51
                  FC Sion  6 14  8 14 55 62  -7 50
              Servette FC  7 13  9 14 76 73   3 48
                FC Luzern  8 14  6 16 75 79  -4 48
            FC Basel 1893  9 11  8 17 66 77 -11 41
                FC Lugano 10 10  5 21 58 63  -5 35
Edit: time-Ausgabe entfernt.

Benutzeravatar
paedubucher
Beiträge: 589
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Scripting-Challenge: Fussballergebnisse in Ligatabelle umwandeln

Beitrag von paedubucher » 16.01.2022 19:55:59

Ich werfe mal R in den Ring:

Code: Alles auswählen

#!/usr/bin/env Rscript

library(jsonlite)
library(readr)

column_names <- c("name", "rank", "wins", "ties", "defeats",
                  "goalsScored", "goalsConceded", "goalsDifference",
                  "points")

create_empty_row <- function(teamName) {
    row <- data.frame(teamName, 0, 0, 0, 0, 0, 0, 0, 0)
    colnames(row) <- column_names
    return(row)
}

args <- commandArgs(trailingOnly=TRUE)
data <- read_file(args[1])
matches <- fromJSON(data)

table <- data.frame(matrix(ncol=9, nrow=0))
colnames(table) <- column_names

for (row in 1:nrow(matches)) {
    homeTeam <- matches[row, "homeTeam"]
    awayTeam <- matches[row, "awayTeam"]
    homeGoals <- matches[row, "homeGoals"]
    awayGoals <- matches[row, "awayGoals"]

    homeIndex <- which(table$name == homeTeam)
    awayIndex <- which(table$name == awayTeam)
    if (length(homeIndex) == 0) {
        table <- rbind(table, create_empty_row(homeTeam))
        homeIndex <- which(table$name == homeTeam)
    }
    if (length(awayIndex) == 0) {
        table <- rbind(table, create_empty_row(awayTeam))
        awayIndex <- which(table$name == awayTeam)
    }

    table[homeIndex, "goalsScored"] <- table[homeIndex, "goalsScored"] + homeGoals
    table[awayIndex, "goalsScored"] <- table[awayIndex, "goalsScored"] + awayGoals
    table[homeIndex, "goalsConceded"] <- table[homeIndex, "goalsConceded"] + awayGoals
    table[awayIndex, "goalsConceded"] <- table[awayIndex, "goalsConceded"] + homeGoals

    if (homeGoals > awayGoals) {
        table[homeIndex, "wins"] <- table[homeIndex, "wins"] + 1
        table[awayIndex, "defeats"] <- table[awayIndex, "defeats"] + 1
    } else if (homeGoals < awayGoals) {
        table[awayIndex, "wins"] <- table[awayIndex, "wins"] + 1
        table[homeIndex, "defeats"] <- table[homeIndex, "defeats"] + 1
    } else {
        table[homeIndex, "ties"] <- table[homeIndex, "ties"] + 1
        table[awayIndex, "ties"] <- table[awayIndex, "ties"] + 1
    }
}

for (row in 1:nrow(table)) {
    table[row, "goalsDifference"] <- table[row, "goalsScored"] - table[row, "goalsConceded"]
    table[row, "points"] <- table[row, "wins"] * 3 + table[row, "ties"]
}

table <- table[order(table$points, table$goalsDifference, table$wins, rev(table$name), decreasing=TRUE),]

for (row in 1:nrow(table)) {
    table[row, "rank"] <- row
}

cat("                     Name  #  w  d  l  +  -   =  P\n")
cat("--------------------------------------------------\n")
for (row in 1:nrow(table)) {
    r = table[row,]
    i = r$rank
    n = r$name
    w = r$wins
    d = r$ties
    l = r$defeats
    gs = r$goalsScored
    gc = r$goalsConceded
    gd = r$goalsDifference
    p = r$points
    format(n, width=25, justify="right")
    line = sprintf("%25s %2d %2d %2d %2d %2d %2d %3d %2d\n", n, i, w, d, l, gs, gc, gd, p)
    cat(line)
}
Die Ausgabe mit Multibyte-Zeichen funktioniert leider nicht ideal:

Code: Alles auswählen

$ ./superleague.R data/super-league.json 
                     Name  #  w  d  l  +  -   =  P
--------------------------------------------------
               FC Zürich  1 16 10 10 70 59  11 58
 Grasshopper Club Zürich  2 17  7 12 79 70   9 58
           BSC Young Boys  3 16 10 10 66 58   8 58
        FC Lausanne-Sport  4 16  6 14 58 63  -5 54
       FC St. Gallen 1879  5 14  9 13 59 58   1 51
                  FC Sion  6 14  8 14 55 62  -7 50
              Servette FC  7 13  9 14 76 73   3 48
                FC Luzern  8 14  6 16 75 79  -4 48
            FC Basel 1893  9 11  8 17 66 77 -11 41
                FC Lugano 10 10  5 21 58 63  -5 35
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

thoerb
Beiträge: 1415
Registriert: 01.08.2012 15:34:53
Lizenz eigener Beiträge: MIT Lizenz

Re: Scripting-Challenge: Fussballergebnisse in Ligatabelle umwandeln

Beitrag von thoerb » 16.01.2022 20:10:42

paedubucher hat geschrieben: ↑ zum Beitrag ↑
16.01.2022 19:55:59
Die Ausgabe mit Multibyte-Zeichen funktioniert leider nicht ideal:

Code: Alles auswählen

$ ./superleague.R data/super-league.json 
                     Name  #  w  d  l  +  -   =  P
--------------------------------------------------
               FC Zürich  1 16 10 10 70 59  11 58
 Grasshopper Club Zürich  2 17  7 12 79 70   9 58
           BSC Young Boys  3 16 10 10 66 58   8 58
        FC Lausanne-Sport  4 16  6 14 58 63  -5 54
       FC St. Gallen 1879  5 14  9 13 59 58   1 51
                  FC Sion  6 14  8 14 55 62  -7 50
              Servette FC  7 13  9 14 76 73   3 48
                FC Luzern  8 14  6 16 75 79  -4 48
            FC Basel 1893  9 11  8 17 66 77 -11 41
                FC Lugano 10 10  5 21 58 63  -5 35
Das gleiche Problem hatte ich in PHP, als ich zum Auffüllen die Funktion str_pad nutzen wollte.

Benutzeravatar
paedubucher
Beiträge: 589
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Scripting-Challenge: Fussballergebnisse in Ligatabelle umwandeln

Beitrag von paedubucher » 16.01.2022 23:47:27

Hat es hier keine Perl-Programmierer? Ich würde das gerne in einem Einzeiler gelöst sehen :D
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
paedubucher
Beiträge: 589
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Scripting-Challenge: Fussballergebnisse in Ligatabelle umwandeln

Beitrag von paedubucher » 17.01.2022 08:20:33

Dann werfe ich mal Lua nach:

Code: Alles auswählen

#!/usr/bin/env lua

local json = require("dkjson")

function new_league_row(team_name)
    return {team = team_name, wins = 0, ties = 0, defeats = 0, goals_scored = 0, goals_conceded = 0}
end

function sort(a, b)
    if a["points"] > b["points"] then
        return true
    elseif a["points"] < b["points"] then
        return false
    elseif a["goals_difference"] > b["goals_difference"] then
        return true
    elseif a["goals_difference"] < b["goals_difference"] then
        return false
    elseif a["wins"] > b["wins"] then
        return true
    elseif a["wins"] < b["wins"] then
        return false
    elseif a["team"] < b["team"] then
        return true
    elseif a["team"] > b["team"] then
        return false
    end
end

matches_path = arg[1]
local matches_file = io.open(matches_path, "r")
local matches_data = matches_file:read("*a")

local matches = json.decode(matches_data)
local league = {}

for _, match in ipairs(matches) do
    home_team = match["homeTeam"]
    away_team = match["awayTeam"]
    home_goals = match["homeGoals"]
    away_goals = match["awayGoals"]

    if league[home_team] == nil then
        league[home_team] = new_league_row(home_team)
    end
    if league[away_team] == nil then
        league[away_team] = new_league_row(away_team)
    end

    league[home_team]["goals_scored"] = league[home_team]["goals_scored"] + home_goals
    league[home_team]["goals_conceded"] = league[home_team]["goals_conceded"] + away_goals
    league[away_team]["goals_scored"] = league[away_team]["goals_scored"] + away_goals
    league[away_team]["goals_conceded"] = league[away_team]["goals_conceded"] + home_goals 

    if home_goals > away_goals then
        league[home_team]["wins"] = league[home_team]["wins"] + 1
        league[away_team]["defeats"] = league[away_team]["defeats"] + 1
    elseif home_goals < away_goals then
        league[home_team]["defeats"] = league[home_team]["defeats"] + 1
        league[away_team]["wins"] = league[away_team]["wins"] + 1
    else
        league[home_team]["ties"] = league[home_team]["ties"] + 1
        league[away_team]["ties"] = league[away_team]["ties"] + 1
    end
end

for team, row in pairs(league) do
    row["goals_difference"] = row["goals_scored"] - row["goals_conceded"]
    row["points"] = row["wins"] * 3 + row["ties"]
end

ordered = {}
for _, row in pairs(league) do
    table.insert(ordered, row)
end
table.sort(ordered, sort)

title = string.format("%25s %2s %2s %2s %2s %2s %2s %3s %2s",
                      "Name", "#", "w", "d", "l", "+", "-", "=", "P")
print(title)
print(string.rep("-", string.len(title)))
for i, row in pairs(ordered) do
    print(string.format("%25s %2d %2d %2d %2d %2d %2d %3d %2d",
                        row["team"], i+1,
                        row["wins"], row["ties"], row["defeats"],
                        row["goals_scored"], row["goals_conceded"], row["goals_difference"],
                        row["points"]))
end
Auch hier wieder das Multibyte-Problem:

Code: Alles auswählen

$ ./superleague.lua data/super-league.json
                     Name  #  w  d  l  +  -   =  P
--------------------------------------------------
               FC Zürich  2 16 10 10 70 59  11 58
 Grasshopper Club Zürich  3 17  7 12 79 70   9 58
           BSC Young Boys  4 16 10 10 66 58   8 58
        FC Lausanne-Sport  5 16  6 14 58 63  -5 54
       FC St. Gallen 1879  6 14  9 13 59 58   1 51
                  FC Sion  7 14  8 14 55 62  -7 50
              Servette FC  8 13  9 14 76 73   3 48
                FC Luzern  9 14  6 16 75 79  -4 48
            FC Basel 1893 10 11  8 17 66 77 -11 41
                FC Lugano 11 10  5 21 58 63  -5 35
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
Meillo
Moderator
Beiträge: 7469
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Scripting-Challenge: Fussballergebnisse in Ligatabelle umwandeln

Beitrag von Meillo » 17.01.2022 08:42:16

Hier kommt meine awk-Implementierung. Das ist diejenige, die ich als erstes geschrieben habe und die mir weiterhin am liebsten ist:

Code: Alles auswählen

#!/bin/sh

cat "$@" | awk -F, -vOFS=, '
{
        tplus[$1] += $2; tminus[$1] += $3
        tplus[$4] += $3; tminus[$4] += $2
        if ($2 > $3) {
                w[$1]++; l[$4]++
        } else if ($3 > $2) {
                l[$1]++; w[$4]++
        } else {
                d[$1]++; d[$4]++
        }
}
END {
        for (i in w) {
                print i, w[i], d[i], l[i], tplus[i], tminus[i],
                                tplus[i] - tminus[i], w[i] * 3 + d[i]
        }
}
' | sort -t, -k8,8nr -k7,7nr -k2,2nr -k1,1 | awk -F, '
BEGIN {
        print "                     Name  #  w  d  l  +  -   =  P"
        print "--------------------------------------------------"
}
{
        printf("%25s %2d %2d %2d %2d %2d %2d %3d %2d\n",
                        $1, NR, $2, $3, $4, $5, $6, $7, $8)
}
'
Use ed(1) once in a while!

reox
Beiträge: 2187
Registriert: 06.06.2006 22:09:47
Lizenz eigener Beiträge: MIT Lizenz

Re: Scripting-Challenge: Fussballergebnisse in Ligatabelle umwandeln

Beitrag von reox » 17.01.2022 13:01:33

noch eine py3 lösung

Code: Alles auswählen

import json
import sys
from collections import defaultdict, namedtuple
from functools import reduce


result = namedtuple("Result", ['club', 'win', 'draw', 'lost', 'score', 'suffer', 'diff', 'points'])


def calc(a, b):
    # Returns (w, d, l, +, -, =, P) for a single match
    return (a > b, a == b, a < b, a, b, a - b, 1 if a == b else (0 if a < b else 3))


if __name__ == "__main__":
    if len(sys.argv) != 2:
        print(f"usage: {sys.argv[0]} file.json", file=sys.stderr)
        sys.exit(1)
    fp = open(sys.argv[1], 'rb') if sys.argv[1] != '-' else sys.stdin
    clubs = defaultdict(list)
    for game in json.load(fp):
        a, b = game['homeGoals'], game['awayGoals']
        clubs[game['homeTeam']].append(calc(a, b))
        clubs[game['awayTeam']].append(calc(b, a))
    fp.close()

    res_data = list()
    for club, res in clubs.items():
        res_data.append(result(club, *reduce(lambda a, b: [x + y for x, y in zip(a, b)], res)))

    print(f"{'Name':>25.25}  #  w  d  l  +  -   =  P")
    print("-" * 50)
    for rank, dat in enumerate(sorted(res_data, key=lambda x: (-x.points, -x.diff, -x.win, x.club)), start=1):
        print(f'{dat.club:>25.25} {rank:>2d} '
              f'{dat.win:>2d} {dat.draw:>2d} {dat.lost:>2d} '
              f'{dat.score:>2d} {dat.suffer:>2d} {dat.diff:>3d} {dat.points:>2d}')
wsl kann man die beiden schleifen auch noch vereinen ;)

edit: special filename '-' fix


edit2: Noch eine variante mit klasse und einer schleife weniger

Code: Alles auswählen

import json
import sys
from collections import defaultdict


class Result:
    __slots__ = ['win', 'draw', 'lost', 'score', 'suffer', 'points']

    def __init__(self):
        for var in self.__slots__:
            setattr(self, var, 0)

    def add(self, a, b):
        self.win += a > b
        self.lost += a < b
        self.draw += a == b
        self.score += a
        self.suffer += b
        self.points += 1 if a == b else (0 if a < b else 3)

    @property
    def diff(self):
        return self.score - self.suffer


if __name__ == "__main__":
    if len(sys.argv) == 1 or (len(sys.argv) == 2 and sys.argv[1] == '-'):
        fp = sys.stdin
    elif len(sys.argv) != 2:
        print(f"usage: {sys.argv[0]} [file.json]", file=sys.stderr)
        sys.exit(1)
    else:
        fp = open(sys.argv[1], 'rb')
    clubs = defaultdict(Result)
    for game in json.load(fp):
        a, b = game['homeGoals'], game['awayGoals']
        clubs[game['homeTeam']].add(a, b)
        clubs[game['awayTeam']].add(b, a)
    fp.close()

    print(f"{'Name':>25.25}  #  w  d  l  +  -   =  P")
    print("-" * 50)
    for rank, (club, dat) in enumerate(sorted(clubs.items(), key=lambda x: (-x[1].points, -x[1].diff, -x[1].win, x[0])), start=1):
        print(f'{club:>25.25} {rank:>2d} '
              f'{dat.win:>2d} {dat.draw:>2d} {dat.lost:>2d} '
              f'{dat.score:>2d} {dat.suffer:>2d} {dat.diff:>3d} {dat.points:>2d}')

Benutzeravatar
Meillo
Moderator
Beiträge: 7469
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Scripting-Challenge: Fussballergebnisse in Ligatabelle umwandeln

Beitrag von Meillo » 20.01.2022 09:14:09

Hier meine C-Variante:

Code: Alles auswählen

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define MAXTEAMS 16

struct team {
        char *name;
        int wins, draws, losses;
        int gmade, gtook;
} teams[MAXTEAMS+1];
int nteams;


struct team *
getteam(char *name)
{
        struct team *p;
        int i=0;

        for (p=teams; p->name; p++, i++) {
                if (i == MAXTEAMS) {
                        fprintf(stderr, "too many teams (only %d allowed)\n",
                                        MAXTEAMS);
                        exit(1);
                }
                if (strcmp(name, p->name)==0) {
                        return p;
                }        /* insert */
        }
        nteams++;
        p->name = strdup(name);
        return p;
}

int
teamcmp(const void *a, const void *b)
{
        const struct team *t1 = a;
        const struct team *t2 = b;

        if (t1->wins*3 + t1->draws > t2->wins*3 + t2->draws) {
                return -1;
        } else if (t1->wins*3 + t1->draws < t2->wins*3 + t2->draws) {
                return 1;
        } else if (t1->gmade - t1->gtook > t2->gmade - t2->gtook) {
                return -1;
        } else if (t1->gmade - t1->gtook < t2->gmade - t2->gtook) {
                return 1;
        } else if (t1->wins > t2->wins) {
                return -1;
        } else if (t1->wins < t2->wins) {
                return 1;
        } else {
                return strcmp(t1->name, t2->name);
        }
}

int
main(void)
{
        char buf[BUFSIZ];
        struct team *t1, *t2, *p;
        int t1g, t2g;
        int place=1;

        while (fgets(buf, sizeof buf, stdin)) {
                /* TODO: error handling for parsing */
                t1 = getteam(strtok(buf, ","));
                t1g = atoi(strtok(NULL, ","));
                t2g = atoi(strtok(NULL, ","));
                t2 = getteam(strtok(NULL, "\n"));

                t1->gmade += t1g; t1->gtook += t2g;
                t2->gmade += t2g; t2->gtook += t1g;

                if (t1g > t2g) {
                        t1->wins++; t2->losses++;
                } else if (t1g < t2g) {
                        t1->losses++; t2->wins++;
                } else {
                        t1->draws++; t2->draws++;
                }
        }

        qsort(teams, nteams, sizeof(teams[0]), teamcmp);

        printf("                     Name  #  w  d  l  +  -   =  P\n");
        printf("--------------------------------------------------\n");
        for (p=teams; p->name; p++) {
                printf("%25s %2d %2d %2d %2d %2d %2d %3d %2d\n",
                                p->name, place++,
                                p->wins, p->draws, p->losses,
                                p->gmade, p->gtook, p->gmade - p->gtook,
                                p->wins*3 + p->draws);
        }

}
Die liest aus Bequemlichkeit nur von Stdin.
Use ed(1) once in a while!

Antworten