I need to split a list into multiple lists that are grouped according to the first character of a string property. Here's an example.
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; }
}
Which outputs:
Acura
AMG
Audi
BMW
Chevrolet
---
Datsun
Eagle
Fiat
---
Honda
Infiniti
Jaguar
---
This algorithm works, but feels very clumsy. Is there a more elegant way to do this?
Using a for loop and range() method, iterate from 0 to the length of the list with the size of chunk as the step. Return the chunks using yield . list_a[i:i+chunk_size] gives each chunk. For example, when i = 0 , the items included in the chunk are i to i + chunk_size which is 0 to (0 + 2)th index.
Thus, the letters A, B, C, D will be in the first group, the letters E, F, G, H will. The English alphabet is divided into five groups. Each group starts with the vowel and the consonants immediately following that vowel and the consonants immediately following that vowel are included in that group.
The easiest way to split list into equal sized chunks is to use a slice operator successively and shifting initial and final position by a fixed number.
To split the elements of a list in Python: Use a list comprehension to iterate over the list. On each iteration, call the split() method to split each string.
Following extention method uses linq to select all the vehicle makes whose name starts in range of characters.
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();
}
USAGE 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));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With