我需要将列表拆分为多个列表,这些列表根据字符串属性的第一个字符进行分组。这是一个例子。
class Program
{
static void Main(string[] args)
{
var makes = new List<VehicleMake>
{
new VehicleMake {Name = "Acura"},
new VehicleMake {Name = "AMG"},
new VehicleMake {Name = "Audi"},
new VehicleMake {Name = "BMW"},
new VehicleMake {Name = "Chevrolet"},
new VehicleMake {Name = "Datsun"},
new VehicleMake {Name = "Eagle"},
new VehicleMake {Name = "Fiat"},
new VehicleMake {Name = "Honda"},
new VehicleMake {Name = "Infiniti"},
new VehicleMake {Name = "Jaguar"}
};
var balancedLists = makes.Balance(new List<BalancedListGroup>
{
new BalancedListGroup { RangeStart = 'A', RangeEnd = 'C'},
new BalancedListGroup { RangeStart = 'D', RangeEnd = 'F'},
new BalancedListGroup { RangeStart = 'G', RangeEnd = 'J'},
});
foreach (var balancedList in balancedLists)
{
foreach (var vehicleMake in balancedList)
{
Console.WriteLine(vehicleMake.Name);
}
Console.WriteLine("---");
}
Console.ReadLine();
}
}
public class VehicleMake
{
public string Name { get; set; }
}
public static class VehicleMakeListBalancer
{
public static List<List<VehicleMake>> Balance(this List<VehicleMake> list, List<BalancedListGroup> groups)
{
var letters =
new List<string> { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "y", "z" };
var balancedLists = new List<List<VehicleMake>>();
foreach (var group in groups)
{
var groupList = new List<VehicleMake>();
for (var i = letters.IndexOf(group.RangeStart.ToString().ToLower()); i <= letters.IndexOf(group.RangeEnd.ToString().ToLower()); i++)
{
groupList.AddRange(list.Where(l => l.Name.ToLower().StartsWith(letters[i].ToString())).ToList());
}
balancedLists.Add(groupList);
}
return balancedLists;
}
}
public class BalancedListGroup
{
public char RangeStart { get; set; }
public char RangeEnd { get; set; }
}
哪个输出:
Acura
AMG
Audi
BMW
Chevrolet
---
Datsun
Eagle
Fiat
---
Honda
Infiniti
Jaguar
---
此算法有效,但感觉非常笨拙。有更优雅的方式吗?
答案 0 :(得分:4)
以下扩展方法使用linq选择名称以字符范围开头的所有车辆品牌。
public static List<VehicleMake> GetInRange(this List<VehicleMake> vehicleList, char RangeStart, char RangeEnd)
{
var vehiclesInRange = from vm in vehicleList
where vm.Name[0] >= RangeStart && vm.Name[0] <= RangeEnd
select vm;
return vehiclesInRange.ToList();
}
使用示例
static class Program
{
static void Main(string[] args)
{
var makes = new List<VehicleMake> {
new VehicleMake { Name = "Acura" },
new VehicleMake { Name = "AMG" },
new VehicleMake { Name = "Audi" },
new VehicleMake { Name = "BMW" },
new VehicleMake { Name = "Chevrolet" },
new VehicleMake { Name = "Datsun" },
new VehicleMake { Name = "Eagle" },
new VehicleMake { Name = "Fiat" },
new VehicleMake { Name = "Honda" },
new VehicleMake { Name = "Infiniti" },
new VehicleMake { Name = "Jaguar" }
};
var atoc = makes.GetInRange('A', 'C');
atoc.Print();
var dtom = makes.GetInRange('D', 'M');
dtom.Print();
var mtoz = makes.GetInRange('M', 'Z');
mtoz.Print();
Console.ReadLine();
}
static List<VehicleMake> GetInRange(this List<VehicleMake> vehicleList, char RangeStart, char RangeEnd)
{
var vehiclesInRange = from vm in vehicleList
where vm.Name[0] >= RangeStart && vm.Name[0] <= RangeEnd
select vm;
return vehiclesInRange.ToList();
}
static void Print(this List<VehicleMake> vehicles)
{
Console.WriteLine();
vehicles.ForEach(v => Console.WriteLine(v.Name));
}
}
答案 1 :(得分:2)
您可以使用GroupBy()
来达到您想要的效果 - 按照您车辆的第一个字母分组,然后列出这些内容:
var balancedLists = makes.GroupBy(x => x.Name[0]).Select( x=> x.ToList())
.ToList();
但是,这将创建仅包含一个字母的组 - 修改分组行为,您可以提供自定义方法GetGroup( char c)
,该方法返回一个整数来标识该组。
或者,如果你想要的只是平衡组,你可以使用索引将车辆分组成相同大小的组:
var balancedLists = makes.Select((vehicle, index) => new { Index = index, Vehicle = vehicle })
.GroupBy(x => x.Index / 3)
.Select(g => g.Select(x => x.Vehicle).ToList())
.ToList();
答案 2 :(得分:1)
如果您有一个包含以下不变量的对象:
然后,您可以借助该对象对您的车辆列表进行分组:
var groups = vehicles.GroupBy(x => rangeContainer.GroupKey(x))
答案 3 :(得分:1)
我相信这个查询会有效地满足您的需求:
var letterGroupTuples
= from blGroup in groups
from letter in Enumerable.Range
(blGroup.RangeStart, blGroup.RangeEnd - blGroup.RangeStart + 1)
select new { Letter = char.ToLower((char)letter), BlGroup = blGroup };
var groupsForLetters = letterGroupTuples.ToDictionary
(a => a.Letter, a => a.BlGroup);
var query = from vehicleMake in list
let key = vehicleMake.Name.ToLower().First()
where groupsForLetters.ContainsKey(key)
group vehicleMake by groupsForLetters[key] into bucket
select bucket.ToList();
return query.ToList();
这个想法是:
答案 4 :(得分:1)
如果你愿意,另一个Linq选项。
var makes = new List<VehicleMake> {
new VehicleMake { Name = "Acura" },
new VehicleMake { Name = "AMG" },
new VehicleMake { Name = "Audi" },
new VehicleMake { Name = "BMW" },
new VehicleMake { Name = "Chevrolet" },
new VehicleMake { Name = "Datsun" },
new VehicleMake { Name = "Eagle" },
new VehicleMake { Name = "Fiat" },
new VehicleMake { Name = "Honda" },
new VehicleMake { Name = "Infiniti" },
new VehicleMake { Name = "Jaguar" } };
var balancedLists = new List<BalancedListGroup>
{
new BalancedListGroup { RangeStart = 'A', RangeEnd = 'C' },
new BalancedListGroup { RangeStart = 'D', RangeEnd = 'F' },
new BalancedListGroup { RangeStart = 'G', RangeEnd = 'J' },
};
List<List<VehicleMake>> brandedMakes = new List<List<VehicleMake>>();
foreach (var x in balancedLists)
{
brandedMakes.Add(makes.Where(a => a.Name.Substring(0, 1)[0] >= x.RangeStart && a.Name.Substring(0, 1)[0] < x.RangeEnd).ToList());
}
答案 5 :(得分:1)
我会把我的2p扔进去:
我开始假设一件非常糟糕的事情,以简化我的代码。即您正在使用大写字母ascii字符作为首字母。显然,我的代码可以修改为适用于您的范围结构。
所以我得到了我的初始范围:
var initialGroups = new List<IEnumerable<char>>
{
Enumerable.Range((int)'A', 3).Select(i => (char)i)
, Enumerable.Range((int)'D', 3).Select(i => (char)i)
, Enumerable.Range((int)'G', 4).Select(i => (char)i)
};
获取组的方法是:
IEnumerable<IEnumerable<string>> GroupByInitial(List<string> cars, List<IEnumerable<char>> initialGroups)
{
var groups = from grp in initialGroups
from car in cars
where grp.Contains(car[0])
select new {grp, car};
return groups.GroupBy(group => group.grp).Select(group => group.Select(grouping => grouping.car));
}