问题
I have a simple class called Team, that looks like this:
public class Team
{
public Team ParentTeam;
public string Name;
}
So it has a Name and a reference to another team that is its Parent Team.
I now have a list of Teams that I am getting back from a function
List<Team> list = GetTeamsList();
Given, a few assumptions:
- All teams have a ParentTeam except one (the top team)
- Every team returned in the list is part of the same hierarchy and its only a single hierarchy (no 2 teams at the same "level")
I now need to take the results of this function and order the list by the hierarchy
So imagine we have the following team information:
|| Team Name || Parent Team Name ||
||-----------||------------------||
|| Team A || Team B ||
|| Team B || Team C ||
|| Team C || Team D ||
|| Team D || null ||
but the GetTeamsList() function returns the teams in any random order. For example, it might come back list this:
var teamA = GetTeamA();
var teamB = GetTeamB();
var teamC = GetTeamC();
var teamD = GetTeamD();
List<Team> list = new List() { teamD, teamA, teamB, teamC };
where I need to reorder this list so it looks like this:
List<Team> list = new List() { teamA, teamB, teamC, teamD };
How could I reorder a list into the "correct" order based on the team hierarchy?
回答1:
Several of the solutions given so far are correct, and all of them are at least quadratic in the number of teams; they will be inefficient as the number of teams grows large.
Here's a solution which is (1) linear, (2) shorter, and (3) easier to understand than some of the other solutions so far:
static IEnumerable<Team> SortTeams(IEnumerable<Team> teams)
{
var lookup = teams.ToDictionary(t => t.ParentTeam ?? new Team());
var current = teams.Single(t => t.ParentTeam == null);
do
yield return current;
while (lookup.TryGetValue(current, out current));
}
This produces the sequence in the reverse of the order you want, so put a Reverse on the end of the call if you want it in the other order:
Console.WriteLine(String.Join(" ", SortTeams(teams).Reverse().Select(t => t.Name)));
The "dummy" team is there because a dictionary does not allow a key to be null.
回答2:
This is my suggestion:
public class Team
{
public Team ParentTeam;
public string Name;
int Level
{
get
{
int i = 0;
Team p = this.ParentTeam;
while (p != null)
{
i++;
p = p.ParentTeam;
}
return i;
}
}
static IEnumerable<Team> Sort(IEnumerable<Team> list)
{
return list.OrderBy(o => o.Level);
}
}
Of course, if there are Teams with equal level, you might use another criteria to sort them.
回答3:
This should work:
static IEnumerable<Team> GetOrdered(IEnumerable<Team> teams)
{
var set = teams as HashSet<Team> ?? new HashSet<Team>(teams);
var current = teams.First(t => t.Parent == null);
while (set.Count > 1)
{
yield return current;
set.Remove(current);
current = set.First(t => t.Parent == current);
}
yield return set.Single();
}
This gives you the reversed order, so you should call Reverse() to get the order you are asking for.
回答4:
We can find the ascendants of the null team, defining an extension
public static IEnumerable<Team> FindAscendants(this IEnumerable<Team> l, Team from)
{
Team t = l.FirstOrDefault(x =>
(x.ParentTeam?.Name ?? "").Equals(from?.Name ?? ""));
return new List<Team>() { t }.Concat(t != null ?
l.FindAscendants(t) : Enumerable.Empty<Team>());
}
and reverse the order of the null team's ascendants
list.FindAscendants(null).Reverse().Skip(1)
Edit
Alternative version of the extension with yield return
public static IEnumerable<Team> FindAscendants(this IEnumerable<Team> l, Team from)
{
Team t = l.FirstOrDefault(x =>
(x.ParentTeam?.Name ?? "").Equals(from?.Name ?? ""));
yield return t;
if (t != null)
foreach (Team r in l.FindAscendants(t))
{
yield return r;
}
}
Edit 2
In terms of the most efficient solution, a dictionary is the key. As you can see now, there is no longer need to reverse the order. So an optimized version would be
public static IEnumerable<Team> FindDescendandOptimized(this List<Team> l, Team from)
{
int count = l.Count;
var dic = l.ToDictionary(x => x.ParentTeam?.Name??"");
Team start = dic[from?.Name??""];
Team[] res = new Team[count];
res[count - 1] = start;
for (int i = count - 2; i >= 0; i--)
{
start = dic[start.Name];
res[i] = start;
}
return res;
}
with a test case and usage
List<Team> list = new List<Team>();
Team team = new Team();
team.Name = "0";
list.Add(team);
for (int i = 1; i < 200000; i++)
{
team = new Team();
team.Name = i.ToString();
team.ParentTeam = list.Last();
list.Add(team);
}
list.Reverse();
Console.WriteLine("Order List of " + list.Count +" teams");
Console.WriteLine("order is " + (TestOrder(list) ? "ok" : "ko"));
list.Shuffle();
Console.WriteLine("Shuffled List");
Console.WriteLine("order is " + (TestOrder(list) ? "ok" : "ko"));
DateTime start = DateTime.Now;
var res = list.FindDescendandOptimized(null);
list = res.ToList();
DateTime end = DateTime.Now;
Console.WriteLine("Reordered List");
Console.WriteLine("order is " + (TestOrder(list) ? "ok" : "ko"));
Console.WriteLine("Benchmark ms: " + (end - start).TotalMilliseconds);
Console.ReadLine();
where the test check is
static bool TestOrder(List<Team> list)
{
int tot = list.Count;
for (int i = 0; i < tot; i++)
{
if (!list[i].Name.Equals((tot-i-1).ToString()))
{
return false;
}
}
return true;
}
Edit 3
A final consideration, maybe obvious. The absolutely most efficient way would have been to define a child team.
public class Team
{
public string Name;
public Team ParentTeam;
public Team ChildTeam;
}
appropriately filled like below
team.ParentTeam = list.Last();
list.Last().ChildTeam = team;
to enable an immediate reordering
DateTime start = DateTime.Now;
var res = list.OrderByChild(); //list.FindDescendandOptimized(null);
list = res.ToList();
DateTime end = DateTime.Now;
Console.WriteLine("Reordered List");
with a direct link
public static IEnumerable<Team> OrderByChild(this List<Team> l)
{
int count = l.Count;
Team start = l.First(x => x.ParentTeam == null);
Team[] res = new Team[count];
res[count - 1] = start;
for (int i = count - 2; i >= 0; i--)
{
start = start.ChildTeam;
res[i] = start;
}
return res;
}
来源:https://stackoverflow.com/questions/42058132/in-c-how-can-i-order-this-list-of-objects-by-which-item-is-greater