2011-04-08 105 views
7

我需要将列表拆分成根据字符串属性的第一个字符分组的多个列表。这是一个例子。将列表拆分成字母范围

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 
--- 

该算法的工作,但感觉很笨拙。有没有更优雅的方式来做到这一点?

回答

4

继extention方法使用LINQ to选择所有车辆进行其名称从字符范围开始。

 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(); 
     } 

使用Sample

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)); 
     } 
    } 
+0

呃,当我开始写这个算法时,我非常接近这个,除了我试图在整个字符串上做这个,而不是第一个字符。尽管这是完美的。谢谢! – 2011-04-08 17:35:43

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(); 
+0

感谢,但这个例子仅由第一个字母分组。我需要这些小组进行排队。在我的例子中,我期待3个列表。其中一个以A-C开始,其中一个以D-F开始,另一个以G-J开始。样本输出中包含三个超链接的行分隔了由我的算法输出的每个列表。 – 2011-04-08 17:25:16

1

如果你有以下的不变量的对象:

  • 可以承载一些balancedlistgroup项目
  • 对于给定的车辆通过返回例如一个整数,为任何车辆倾倒在给定范围

然后你可以与该对象的援助小组的车辆列表中的一样:

var groups = vehicles.GroupBy(x => rangeContainer.GroupKey(x)) 
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(); 

的想法是:

  1. 有效的字母创建一个散列表到关联的存储桶。
  2. 使用哈希表将项目分组到合适的存储桶中,过滤掉那些没有相应存储桶的项目。
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()); 
     } 
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)); 
}