Reputation: 3070
Say i have a List with items
-17" Screen
- 100GB HD
-10788 Firewire
-Lock Cable
-Monitor
-Mouse
-Keyboard
- USB
I want to iterate a list for items starting from A to c, D to F and so on...
I want this to run for items starting from A to items starting C
@foreach (var item in Model.Items.OrderBy(i => i.Title))
{
// Code
}
I want this to run for items starting from D to items starting F
@foreach (var item in Model.Items.OrderBy(i => i.Title))
{
// Code
}
Any Help ?
Upvotes: 0
Views: 587
Reputation: 1038780
I would start by defining a view model (as always):
public class MyViewModel
{
public string LetterRange { get; set; }
public string[] Titles { get; set; }
}
then a controller action that will convert fetch the model from somewhere and then map it to a view model.
Remark: in this example I will put the mapping code between the model and the view model inside the controller action but normally this should go into a separate mapping layer. For example if you use AutoMapper that could be a great place.
So:
public ActionResult Index()
{
// The model could be of any form and come from anywhere but
// the important thing is that at the end of the day you will have
// a list of titles here
var model = new[]
{
"17\" Screen",
"100GB HD",
"10788 Firewire",
"Lock Cable",
"Monitor",
"Mouse",
"Keyboard",
"USB"
};
// Now let's map this domain model into a view model
// that will be adapted to the requirements of our view.
// And the requirements of this view is to group the titles
// in ranges of 3 letters of the alphabet
var viewModel = Enumerable
.Range(65, 26)
.Select((letter, index) => new
{
Letter = ((char)letter).ToString(),
Index = index
})
.GroupBy(g => g.Index / 3)
.Select(g => g.Select(x => x.Letter).ToArray())
.Select(range => new MyViewModel
{
LetterRange = string.Format("{0}-{1}", range.First(), range.Last()),
Titles = model
.Where(item => item.Length > 0 && range.Contains(item.Substring(0, 1)))
.ToArray()
})
.ToArray();
// Let's add those titles that weren't starting with an alphabet letter
var other = new MyViewModel
{
LetterRange = "Other",
Titles = model.Where(item => !viewModel.Any(x => x.Titles.Contains(item))).ToArray()
};
// and merge them into the final view model
viewModel = new[] { other }.Concat(viewModel).ToArray();
return View(viewModel);
}
and now all that's left in the corresponding view is to display the titles according to the requirements:
@model MyViewModel[]
@foreach (var item in Model)
{
<h2>@item.LetterRange</h2>
foreach (var title in item.Titles)
{
<div>@title</div>
}
}
and the result:
And after refactoring the mapping logic into a mapping layer here's how the corresponding controller action might look like:
public ActionResult Index()
{
// The model could be of any form and come from anywhere but
// the important thing is that at the end of the day you will have
// a list of titles here
DomainModel[] items = ...
// Now let's map this domain model into a view model
// that will be adapted to the requirements of our view.
var viewModel = Mapper.Map<IEnumerable<DomainModel>, IEnumerable<MyViewModel>>(items);
return View(viewModel);
}
Clean and dry.
Upvotes: 4
Reputation: 16623
Why not:
var fromAtoC = Model.Items.Where(x => x.Title != null && x.Title[0] >= 'A' && x.Title[0] <= 'C');
foreach(Model.Items m in fromAtoC)
{
//Do some stuff
}
var fromDtoF = Model.Items.Where(x => x.Title != null && x.Title[0] >= 'D' && x.Title[0] <= 'F');
for(Model.Items m in fromDtoF)
{
//Do some stuff
}
Upvotes: 1
Reputation: 2562
Not sure this is the most elegant solution, but it'll work. I'll see if I can think of something nicer...
var itemsAtoC = Model.Items.Where.Where(i => i.StartsWith("A") || i.StartsWith("B") || i.StartsWith("C"));
foreach(var item in itemsAtoC)
{
Console.Write(item);
}
Ugggh This doesn't really deal with the numerics either...
Still thinking...
Upvotes: 0
Reputation: 2039
try this:
Model.Items.Sort((x, y)=> x.Title.CompareTo(y.Title));
foreach (var item in Model.Items.Where(x => x.Title[0] >= 'A' && x.Title[0] <= 'C')
{
//code
}
Upvotes: 0