I have a list of Options. From that list:
First I want to get only Drink (the where clause).
Then I want to group them by their
SortOrder
(range of 100).I can stop there and loop through the items and create checkboxes for each items. But I got a requirement that if the OptionGroupName=Alcohol then I need to create radio list instead of checkboxes.
Here's the code that does grouping using Linq:
Option[] options = GetOptions();
var groupedOptions = from option in options
where option.OptionType == OptionType.Drinks
group option by option.SortOrder / 100 into grouped
select new {
SortOrderGroup = grouped.Key,
GroupName = grouped.FirstOrDefault().OptionGroupName
,SubGroup= grouped
};
Here's the JSON I get. That's what I want:
So now I can check on client-side if GroupName="Alcohol" generate radio buttons else generate checkboxes.
[
{
"SortOrderGroup": 3,
"GroupName": "Soft",
"SubGroup": [
{
"OptionId": 1,
"DisplayName": "Coke",
"OptionType": 2,
"SortOrder": 300,
"OptionGroupName": "Soft"
},
{
"OptionId": 2,
"DisplayName": "Pepsi",
"OptionType": 2,
"SortOrder": 301,
"OptionGroupName": "Soft"
},
{
"OptionId": 3,
"DisplayName": "Sprite",
"OptionType": 2,
"SortOrder": 302,
"OptionGroupName": "Soft"
}
]
},
{
"SortOrderGroup": 4,
"GroupName": "Fruit",
"SubGroup": [
{
"OptionId": 4,
"DisplayName": "Apple Juice",
"OptionType": 2,
"SortOrder": 406,
"OptionGroupName": "Fruit"
},
{
"OptionId": 1,
"DisplayName": "Orange Juice",
"OptionType": 2,
"SortOrder": 400,
"OptionGroupName": "Fruit"
}
]
},
{
"SortOrderGroup": 5,
"GroupName": "Alcohol",
"SubGroup": [
{
"OptionId": 2,
"DisplayName": "Wine",
"OptionType": 2,
"SortOrder": 501,
"OptionGroupName": "Alcohol"
},
{
"OptionId": 3,
"DisplayName": "Beer",
"OptionType": 2,
"SortOrder": 502,
"OptionGroupName": "Alcohol"
},
{
"OptionId": 4,
"DisplayName": "Rum",
"OptionType": 2,
"SortOrder": 520,
"OptionGroupName": "Alcohol"
}
]
}
]
1 Answer 1
So we're looking at a LINQ query:
Option[] options = GetOptions(); var groupedOptions = from option in options where option.OptionType == OptionType.Drinks group option by option.SortOrder / 100 into grouped select new { SortOrderGroup = grouped.Key, GroupName = grouped.FirstOrDefault().OptionGroupName ,SubGroup= grouped };
You went with query syntax - let's see what the method syntax would look like:
var options = GetOptions(); // some IEnumerable - who cares that it's an array?
options.Where(option => option.OptionType == OptionType.Drinks)
.GroupBy(option => option.SortOrder / 100)
.Select(grouped => new
{
Grouping = grouped.Key,
Name = grouped.FirstOrDefault().OptionGroupName),
SubGroup = grouped
});
The FirstOrDefault()
call will return a null
when grouped
has no items - that's not really a possibility here, but just because FirstOrDefault
itself can return a null
, accessing .OptionGroupName
on it looks like it's asking for a NullReferenceException
. It would be better to use .First()
here, because you know a group only exists when it has at least one item...
...but then, you already have an OptionGroupName
for each item - why bother selecting the .First
at all?
I'd probably write your query like this:
options.Where(option => option.OptionType == OptionType.Drinks)
.GroupBy(option => new
{
SortOrder = option.SortOrder / 100, // integer division
GroupName = option.OptionGroupName
})
.Select(grouping => new
{
SortOrder = grouping.Key.SortOrder,
Name = grouping.Key.GroupName,
SubGroup = grouping
});
Notice I don't care what type GetOptions()
is returning - all I'm interested in, is that whatever it's returning implements IEnumerable<T>
so that I can LINQ over it - and the plural name is all I need to see to infer I'm dealing wiht an IEnumerable<T>
:)
GetOptions
shouldn't be returning an array (a concrete type) - it should return an abstraction - IEnumerable<T>
in this case.
-
1\$\begingroup\$ The GetOptions is a 3rd party service call that returns an Array. I will try out your suggestion. \$\endgroup\$gbs– gbs2015年03月23日 20:02:11 +00:00Commented Mar 23, 2015 at 20:02