Проверять существующую подстроку даже при переключении позиций

У меня есть входной текстовый файл следующим образом:

Bunny Gigantus -vs- Dog Majors
Bunny Gigantus Corners -vs- Dog Majors Corners
Bunny Gigantus Penalty -vs- Dog Majors Penalty
Duck -vs- Cat
Tiger -vs- Lion
Tiger Corners -vs Lion Corners

Мне нужно создать выходной текстовый файл, который присваивает (исходный идентификатор совпадения id) для этих совпадений, поэтому ожидаемый результат будет таким:

1: Bunny Gigantus -vs- Dog Majors
1-1: Bunny Gigantus Corners -vs- Dog Majors Corners
1-2: Bunny Gigantus Penalty -vs- Dog Majors Penalty
2: Duck -vs- Cat
3: Tiger -vs- Lion
3-1: Tiger Corners -vs Lion Corners

Поскольку Bunny Gigantus vs Dog Majors играют друг с другом три раза, поэтому они получили 1-1 или 1-2 в этом формате («исходный идентификатор матча - количество раз, которые они встречались до сих пор»). Сначала я думал о Dictionary<int,string>, но поиск дубликатов в словаре кажется странным.

Я реализовал что-то вроде древовидной структуры с родительским классом, подобным этому

public class Parent
{
    public string firstTeamName { get; set; }
    public int firstDigit { get; set; }
    public int secondDigit { get; set; }
    public bool firstAppearance { get; set; }

    public Parent(string _firstTeamName, int _firstDigit)
    {
        firstTeamName = _firstTeamName;
        firstDigit = _firstDigit;
        secondDigit = 1;
        firstAppearance = true;
    }
}

Основной код:

const string readPath = @"inputPath";
const string writePath = @"outPutpath";
string[] lines = File.ReadAllLines(readPath);
            List<Match> listOfTeamsAlreadyPlayed = new List<Match>();
            List<Parent> parents = new List<Parent>();


            int firstDigit = 1;

            foreach (string line in lines)
            {
                string firstTeamInMatch = line.Substring(0, line.IndexOf("-vs-")).Replace("Corners", "").Replace("Penalty", "").Trim();
                string secondTeamInMatch = line.Substring(line.IndexOf("-vs-") + 5).Replace("Corners", "").Replace("Penalty", "").Trim();
                if (!parents.Any(x => (x.firstTeamName == firstTeamInMatch && x.secondTeamName == secondTeamInMatch)))
                {
                    parents.Add(new Parent(firstTeamInMatch, secondTeamInMatch, firstDigit));
                    firstDigit++;
                }
                listOfTeamsAlreadyPlayed.Add(new Match(firstTeamInMatch, secondTeamInMatch));

            }
            int index = 0;
            foreach (Match setOfTeams in listOfTeamsAlreadyPlayed)
            {
                Parent parent = parents.Where(o => (o.firstTeamName == setOfTeams.firstTeam && o.secondTeamName == setOfTeams.secondTeam)).FirstOrDefault();

                if (parent.firstAppearance)
                {
                    lines[index] = parent.firstDigit + ":" + lines[index];
                    parent.firstAppearance = false;
                }

                else
                {
                    lines[index] = parent.firstDigit + "-" + parent.secondDigit + ":" + lines[index];
                    parent.secondDigit++;
                }
                index++;
            }
            using (StreamWriter writer = new StreamWriter(writePath, false))
            {
                foreach (string line in lines)
                {
                    writer.WriteLine(line);
                }
            }

Этот код позволяет мне выполнять эту работу, однако появилось новое требование, которое заключается в том, чтобы убедиться, что даже во входном файле имена переключаются вместе, они все еще распознаются как уже воспроизведенные. Итак, если у меня есть входной файл, подобный этому:

Bunny Gigantus -vs- Dog Majors
Bunny Gigantus Corners -vs- Dog Majors Corners
Bunny Gigantus Penalty -vs- Dog Majors Penalty
Duck -vs- Cat
Tiger -vs- Lion
Tiger Corners -vs Lion Corners
Dog Majors -vs- Bunny Gigantus
Dog Majors Corners -vs- Bunny Gigantus Corners

Будет распознано, что Dog Majors уже встречался с Bunny Gigantus, поэтому желаемый выходной файл должен быть таким:

1: Bunny Gigantus -vs- Dog Majors
1-1: Bunny Gigantus Corners -vs- Dog Majors Corners
1-2: Bunny Gigantus Penalty -vs- Dog Majors Penalty
2: Duck -vs- Cat
3: Tiger -vs- Lion
3-1: Tiger Corners -vs Lion Corners
1: Dog Majors -vs- Bunny Gigantus
1-1: Dog Majors Corners -vs- Bunny Gigantus Corners

Однако с кодом, который у меня есть до сих пор, он совершит ошибку и распознает совпадение переключенного имени как новое совпадение. Таким образом, выходной файл будет таким:

1: Bunny Gigantus -vs- Dog Majors
1-1: Bunny Gigantus Corners -vs- Dog Majors Corners
1-2: Bunny Gigantus Penalty -vs- Dog Majors Penalty
2: Duck -vs- Cat
3: Tiger -vs- Lion
3-1: Tiger Corners -vs Lion Corners
4: Dog Majors -vs- Bunny Gigantus
4-1: Dog Majors Corners -vs- Bunny Gigantus Corners

Что вы предлагаете мне решить эту проблему?

При работе, если вы уже видели определенную пару команд, поменяйте местами названия команд, если A больше, чем B. «Собака» больше, чем «Кролик», поэтому сравните «Кролик против собаки» (уже видел)

Caius Jard 22.12.2020 08:23
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
81
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Требования не совсем ясны, но я сделаю, как мне кажется, разумные предположения.

Я вижу, вы заметили, что ваш первый подход не сработал, потому что он полностью основывался на названии первой команды. Вы обновили пример кода, но забыли включить новый код для Parent и Match. Я собираюсь предположить, что они выглядят примерно так:

public record Parent(string firstTeamName, string secondTeamName, int firstDigit)
{
    public int secondDigit { get; set; } = 1;
    public bool firstAppearance { get; set; } = true;
}
public record Match(string firstTeam, string secondTeam);

Вы заметили, что вам нужно определить Match на основе комбинации двух команд. И исходя из «нового требования», вам нужно, чтобы не имело значения, какая команда появляется первой. Один очень простой способ сделать это — упорядочить команды по их названию, а не по порядку, в котором они появляются.

var teams = line.Split("-vs-", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
    .OrderBy(t => t)
    .ToList();
string firstTeamInMatch = teams[0].Replace("Corners", "").Replace("Penalty", "").Trim();
string secondTeamInMatch = teams[1].Replace("Corners", "").Replace("Penalty", "").Trim();

Теперь Dog Majors -vs- Bunny Gigantus имеет идентификатор 1-3 вместо 4. Но это все равно не правильно, потому что вам действительно нужно следить за тем, чтобы это действительно был именно тот матч, который вы видели раньше. Для этого каждый Матч должен учитывать специальный модификатор ("Угловые"/"Пенальти"), связанный с его командами, и вам нужно будет присвоить первую и вторую цифры каждому отдельному Матчу, который происходит. Есть разные способы сделать это. Вот что я бы сделал:

Я собираюсь использовать объектно-ориентированный подход, в частности используя record, которые доступны в C# 9, потому что они имеют автоматическую реализацию методов равенства и тому подобное. Вы могли бы сделать то же самое, просто используя методы, которые возвращают строки, но это помогает мне легче рассуждать о логике.

record Match(Team Team1, Team Team2)
{
    public static Match Parse(string line)
    {
        var teamNames = line.Split("-vs-", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
        .OrderBy(s => s)
        .Select(Team.Parse)
        .ToList();
        return new Match(teamNames[0], teamNames[1]);
    }
    public Match Base() => this with
    {
        Team1 = Team1.Base(),
        Team2 = Team2.Base(),
    };
    public bool IsSpecial => Team1.Special != null;
}
record Team(string Name, string? Special)
{
    public static Team Parse(string teamStr) =>
        teamStr.EndsWith("Corners") || teamStr.EndsWith("Penalty")
            ? new Team(teamStr.Substring(0, teamStr.Length - 8), teamStr.Substring(teamStr.Length - 7, 7))
            : new Team(teamStr, null);
    public Team Base() => this with { Special = null };
}

Как вы видите, я интерпретирую «Угловые» и «Пенальти» как специальные модификаторы для «Основного» матча и предполагаю, что эти модификаторы всегда будут одинаковыми для обеих команд в матче, основываясь на примере, который вы вводите. я дал. Моя «база» — это то, что вы называете «родителем»: это просто совпадение, в котором нет модификаторов, поэтому я решил повторно использовать тот же класс Match, а не определять новый.

Вместо того, чтобы использовать циклы for и пытаться отслеживать текущее состояние различных индексов, я решил использовать LINQ. Мне легче отлаживать, так как я могу использовать такой инструмент, как LINQPad, чтобы увидеть результаты каждой новой коллекции, которую я создаю, и проверить, выглядит ли эта коллекция так, как я думаю, она должна быть в этот момент - намного проще, чем пытаться пройти через циклы. и отслеживать кучу значений переменных.

var matchLines = lines
    .Select(line => new
    {
        original = line,
        match = Match.Parse(line)
    }).ToList();
var matches = matchLines.Select(e => e.match).ToList();
var matchGroups = matches
    .GroupBy(g => g.Base())
    .Select((bg, bgi) => new
    {
        Base = bg.Key,
        BaseIndex = bgi,
        Matches = bg
            .Where(m => m.IsSpecial)
            .Distinct()
            .Select((m, i) => new { Match = m, Index = i })
    }).ToList();
var matchIndexes = matchGroups
    .Select(g => new { Match = g.Base, Index = (g.BaseIndex + 1).ToString()})
    .Concat(
        matchGroups.SelectMany(g => g.Matches, (g, v) => new
        {
            Match = v.Match,
            Index = (g.BaseIndex + 1) + "-" + (v.Index + 1)
        })
    )
    .ToDictionary(e => e.Match, e => e.Index);

var output = matchLines.Select(e => matchIndexes[e.match] + ": " + e.original )
    .ToList();

Как видите, я создаю Словарь, как вы изначально думали, но вместо Dictionary<int, string> это Dictionary<Match, string>. Таким образом, когда я возвращаюсь к совпадениям, я могу быстро найти строку идентификатора, которую я решил использовать для этого конкретного Match.

спасибо за новогодний подарок. Пожалуйста, извините мою запоздалую благодарственную записку.

Noelia 28.12.2020 05:34

Другие вопросы по теме