Циклическая генерация PHP создает «дубликаты»

Я реализовал циклический генератор на PHP для создания приспособлений между пользователями в лиге. Это сопоставляет всех пользователей в лиге и сталкивает их друг с другом каждую игровую неделю.

Код выглядит так;

    /**
     * Rotates an array for the round robin algorithm
     */
    function round_robin_array($array)
    {
        // we always keep index 0
        $top = array_shift($array);
        $last = array_pop($array);
        $rotate = [$last];
        foreach ($array as $_value) {
            $rotate[] = $_value;
        }
        array_unshift($rotate, $top);
        return $rotate;
    }

    /**
     * Runs a round robin to make a schedule.
     */
    function round_robin($users, $weeks)
    {
        $schedule = [];
        $count = count($users);
        foreach ($users as $_u) {
            $schedule[$_u] = array_fill(0, $weeks, []);
        }
        for ($i=0;$i<$weeks;$i++) {
            for ($a=0;$a<($count / 2) + 1;$a++) {
                $vs = $users[$a];
                $opp = $users[($count - $a) - 1];
                $at = rand(0,4);
                $pg = [$opp, $at];
                $og = [$vs, $at];
                $schedule[$vs][$i] = $pg;
                $schedule[$opp][$i] = $og;
            }
            $users = $this->round_robin_array($users);
        }
        return $schedule;
    }

    public function generateFixtures($league, $users, $weeks)
    {
        $array = [];

        foreach($users as $user) {
            array_push($array, $user['id']);
        }

        if(count($array) % 2 != 0) {
            array_push($array, '0');
        }

        $fixtures = $this->round_robin($array, $weeks);

        $gameweek = 1;

        foreach($fixtures as $key => $val) {
            if($key != 0) {
                foreach($val as $opponent) {
                    LeagueFixture::firstOrCreate([
                        'gameweek' => $gameweek,
                        'league_id' => $league,
                        'user_id' => $key,
                        'opponent_id' => $opponent[0],
                    ]);
                    $gameweek = $gameweek+1;
                }
            }
            $gameweek = 1;
        }

        return $fixtures;

    }

В функцию generateFixtures передаются пользователи лиги, сама лига и количество недель.

Проблема в том, что это создает «дубликаты» для каждого прибора, поскольку, по сути, у каждого пользователя есть представление - например;

Игровая неделя 1

  • Пользователь_1 VS Пользователь_2
  • Пользователь_3 VS Пользователь_4
  • Пользователь_2 VS Пользователь_1
  • Пользователь_4 VS Пользователь_3

Как видите, последние два фикстура там разные; но то же самое!

Мой вопрос заключается в том, является ли это проблемой циклического перебора или я могу/должен отфильтровывать эти дубликаты при их извлечении в контроллере/представлении.


person Alex    schedule 27.05.2015    source источник
comment
создайте копию массива, найдите в нем пары. Найдя пару, удалите их из копии, затем найдите следующую пару. Продолжайте, пока не исчерпаете себя.   -  person Mike 'Pomax' Kamermans    schedule 27.05.2015
comment
Спасибо @Mike'Pomax'Kamermans. Когда вы предлагаете мне это сделать? Во время создания циклического перебора или при рендеринге данных для вывода представления?   -  person Alex    schedule 27.05.2015
comment
лучший способ избежать этого - использовать упорядоченные пары, то есть упорядоченные пары, т.е. для каждой пары (p1, p2) p1 ‹ p2, если вы сохраните это условие в своей конструкции, у вас не будет проблемы   -  person Nikos M.    schedule 27.05.2015
comment
@Alex: при создании. Не генерируйте данные только для того, чтобы потом отклонить их: настройте код так, чтобы он никогда не генерировал эти данные.   -  person Mike 'Pomax' Kamermans    schedule 27.05.2015


Ответы (1)


Это не самое элегантное решение, но в момент ввода данных в базу данных я могу проверить, существует ли уже противоположное тому, что должно быть создано;

            foreach($fixtures as $key => $val) {
                if($key != 0) {
                    //loop through the values to create an entry for each
                    foreach($val as $opponent) {
                        //check if the opposite of each entry already exists
                        if(!LeagueFixture::where('gameweek', $gameweek)
                            ->where('league_id', $league)
                            ->where('user_id', $opponent[0])
                            ->where('opponent_id', $key)
                            ->exists()) {
                            //if not, we can create the entry as usual
                            LeagueFixture::firstOrCreate([
                                'gameweek' => $gameweek,
                                'league_id' => $league,
                                'user_id' => $key,
                                'opponent_id' => $opponent[0],
                            ]);
                        }
                        $gameweek = $gameweek+1;
                    }
                }
                $gameweek = 1;
            }

Сейчас это самое простое решение для меня. Хотя данные генерируются в описанном выше циклическом переборе (и вы можете легко возразить, что это плохая практика), повторяющиеся данные никогда не попадают в мою базу данных.

person Alex    schedule 27.05.2015