У меня есть входной текстовый файл следующим образом:
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
Что вы предлагаете мне решить эту проблему?
Требования не совсем ясны, но я сделаю, как мне кажется, разумные предположения.
Я вижу, вы заметили, что ваш первый подход не сработал, потому что он полностью основывался на названии первой команды. Вы обновили пример кода, но забыли включить новый код для 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
.
спасибо за новогодний подарок. Пожалуйста, извините мою запоздалую благодарственную записку.
При работе, если вы уже видели определенную пару команд, поменяйте местами названия команд, если A больше, чем B. «Собака» больше, чем «Кролик», поэтому сравните «Кролик против собаки» (уже видел)