Below is my Enumerator List:
public enum StatusEnum
{
Open = 1,
Rejected = 2,
Accepted = 3,
Started = 4,
Completed = 5,
Cancelled = 6,
Assigned = 7
}
I need to bind this to a Combobox, but, only show a few specific statuses and ignore the rest.
This is what I have so far:
public static List<Activity.StatusEnum> StatusList()
{
IEnumerable<Activity.StatusEnum> query = Enum.GetValues(typeof(Activity.StatusEnum)).Cast<Activity.StatusEnum>()
.Where(x => x == Activity.StatusEnum.Open
|| x == Activity.StatusEnum.Rejected
|| x == Activity.StatusEnum.Accepted
|| x == Activity.StatusEnum.Started);
return query.ToList();
}
However, I feel that the code is little messy and is not a correct approach to bind filtered Enum list to a Combobox.
Can anyone suggest a more robust way of doing this?
Update
I might need to change the Order of selection. So I need a generic solution which doesn't only get the first X number of statuses.
return Enum.GetValues(typeof(Activity.StatusEnum)).Cast<Activity.StatusEnum>().Where((n, x) => x < 4);
If you want to be able to change the list of items, just add them into a List<Activity.StatusEnum> and use Contains:
var listValid = new List<Activity.StatusEnum>() { Activity.StatusEnum.Open, Activity.StatusEnum.Rejected, Activity.StatusEnum.Accepted, Activity.StatusEnum.Started };
return Enum.GetValues(typeof(Activity.StatusEnum)).Cast<Activity.StatusEnum>().Where(n => listValid.Contains(n));
Well if you're going to hard code the items that should be in the list anyway, why not just do this:
public static List<Activity.StatusEnum> StatusList()
{
return new List<Activity.StatusEnum>
{
Activity.StatusEnum.Open,
Activity.StatusEnum.Rejected,
Activity.StatusEnum.Accepted,
Activity.StatusEnum.Started
};
}
You could also dispose of the List<T> and just return the array itself. As long as you know these are the items you want, then there's no need for Linq.
Steps:
Get the enum values and cast the results to the type of the enum
Sort the enum values by their integer values (otherwise they sort
naturally by unsigned magnitude)
Take the first 4
Code:
return Enum.GetValues(typeof(Activity.StatusEnum))
.Cast<Activity.StatusEnum>()
.OrderBy(se =>(int)se)
.Take(4);
Output:
Open Rejected Accepted Started
First, if possible, I'd make your enum values powers of 2, so they could be OR'd together.
public enum StatusEnum
{
Open = 1,
Rejected = 2,
Accepted = 4,
Started = 8,
Completed = 16,
Cancelled = 32,
Assigned = 64
}
Then you could do something like this:
public static List<Activity.StatusEnum> StatusList()
{
var statusesToShow = Activity.StatusEnum.Open | Activity.StatusEnum.Rejected | Activity.StatusEnum.Accepted | Activity.StatusEnum.Started;
return Enum
.GetValues(typeof(Activity.StatusEnum))
.Cast<Activity.StatusEnum>()
.Where(x => (x & statusesToShow) == x)
.ToList();
}
EDIT: In light of the fact that you can't change the enum values, I'd just recommend you use something like:
public static List<Activity.StatusEnum> StatusList()
{
return new List<Activity.StatusEnum> {
Activity.StatusEnum.Open,
Activity.StatusEnum.Rejected,
Activity.StatusEnum.Accepted,
Activity.StatusEnum.Started
};
}
". . . only show the first 4 statuses and ignore the rest."
To get the first n elements of an IEnumerable<T>, use the Take method:
return Enum.GetValues(typeof(Activity.StatusEnum))
.Cast<Activity.StatusEnum>()
.Take(4)
.ToList();
How about something along the lines of:
.Where(x => x <= Activity.StatusEnum.Started)
Related
I have a Linq query where I am getting response values to a set of questions. I group the response data by question, and then perform various aggregations on the responsedata, such as averaging them. I also count the proportion of responses that could be classified as "high", "middle" and "low", based on specifying response option value ranges.
var result = ItemResponses
.Where (ir => ir.ItemID < 4 && ir.AssessmentInstance.ProjectID == 5)
.Select (ir => ir)
.GroupBy (ir => new {
ir.ItemID
}).Select (grouped => new {
ItemID = grouped.Key.ItemID,
Average = (double)grouped.Average (g => g.OptionValue),
ProportionHighScore =
(double)grouped.Where(g => g.OptionValue == 5 || g.OptionValue == 6).Count()
/ (double)grouped.Count(),
});
I would like to move the code where I specify which optionvalues should be combined into a "high" response category away from the Linq query, and was considering to set up an extension method to do this. In the extension method I can then specify different combinations of response option values that I can score into a "high" score, across scenarious (for example, if the max response was not 6 but 10, then I would count options 8, 9, and 10 towards a "high" response category.
The extension method might look something like this:
public static double ProportionHighScore(this IGrouping<a,b> values, int ResponseOptionID)
{
double ret = 0;
switch (ResponseOptionID)
{
case 1:
//code here to combine response options 5 and 6, and divide by total
break;
case 2:
//code here to combine response options 8, 9 and 10 and divide by total
break;
case 3:
//etc..
break;
default:
break;
}
return ret;
}
But the question I have then is: how to I go about passing the Linq grouping values as a parameter into the extension method? The type of the IGrouping b is an anonymous type.
Update:
I like the idea of just doing GroupBy (ir => it.ItemID) so that I get access to "IGrouping<int, ItemResponse>". But in the code here I simplified a bit. In my actual code there are a few more things going on, such as reversing the OptionValue scores if an item is flagged as "IsReversed".
var result2 = ItemResponses
.Where (ir => ir.ItemID < 4 && ir.AssessmentInstance.ProjectID == 5)
.Select (ir => new {
ItemID = ir.ItemID,
OptionValue =
(
//Reverse option value of items that are flagged to require reverse scoring
ir.Item.IsReversed == 0 ? ir.OptionValue :
((ir.ResponseScaleOption.ResponseScale.MaxValue + 1) - ir.OptionValue)
),
})
.GroupBy (g => new {g.ItemID})
.Select (grouped => new {
ItemID = grouped.Key.ItemID,
Average = (double)grouped.Average (g => g.OptionValue),
ProportionHighScore =
(double)grouped.Where(g => g.OptionValue == 5 || g.OptionValue == 6).Count()
/ (double)grouped.Count(),
});
In some versions of this query I also include fields from some joined tables as well. So the need to reverse the OptionValues is one reason why I assumed I needed an anonymous type. Perhaps I need to create a new class that I can project into ("ItemResponseForAggregation", or some such name), and then be able to do IGrouping<int, ItemResponseForAggregation> for my extension parameter?
You can't pass anonymous types in this way. They have to be in the immediate execution context to treat them as if they were strong types. Create a lightweight type so you can pass this in, and then add a parameter constraint on parameter 'b' to enforce that it must be of the type you've created.
I did a mock up in LINQPad of what I think you're looking for (if I am understanding the requirements correctly):
void Main()
{
var ItemResponses = new List<ItemResponse>();
var result = ItemResponses
.Where(ir => ir.ItemID < 4 && ir.AssessmentInstance.ProjectID == 5)
.GroupBy(ir => ir.ItemID)
.Select(
grouped => new {
ItemID = grouped.Key,
Average = (double)grouped.Average(g => g.OptionValue),
ProportionHighScore = grouped.ProportionHighScore(1, 2, 3, 4, 5)
}
);
result.Dump();
}
public class ItemResponse
{
public int ItemID { get; set; }
public int OptionValue { get; set; }
public AssessmentInstanceItem AssessmentInstance { get; set; }
}
public class AssessmentInstanceItem
{
public int ProjectID { get; set; }
}
public static class ItemResponseExtensions
{
public static double ProportionHighScore(this IGrouping<int, ItemResponse> values, params int[] ResponseOptionID)
{
double count = 0;
double total = values.Count();
foreach (int r in ResponseOptionID)
count += (double)values.Where(g => g.OptionValue == r).Count();
return count / total;
}
}
In the extension method, the params parameter allows you to specify as many options as you need. Then I am just looping through the options, adding the count for each response option.
I need to get values from enum which is not equal to 1 and display them in to dropdown list.
I mean is that, I do not want to show Done and it's value.
public enum Statement
{
Done= 1,
Waiting= 2,
Rejected= 3
}
You can use Enum.GetValues with LINQ like:
List<string> list = Enum.GetValues(typeof(Statement))
.Cast<Statement>()
.Where(r=> (int) r != 1)
.Select(t=> t.ToString())
.ToList();
Given the following:
public class Foo
{
/* other properties */
public Int32 Id { get; set; }
}
var listOfFoo = new[]{
new Foo { Id = 1 },
new Foo { Id = 2 },
new Foo { Id = 3 }
};
var sortOrderIds = new[]{
2, 3, 1
};
If I wanted to sort listOfFoo to have the Ids end up in the same order as presented in sortOrderIds, what's the best way? I assume I could sort using something like:
Int32 SortUsingIdArrayAsReference(Foo x, Foo y)
{
// creative license on "IndexOf", bear with me
return Int32.CompareTo(sortOrderids.IndexOf(x.Id), sortOrderIds.indexOf(y.Id));
}
But is that really the best way to do this? I was hoping LINQ may have something better I could use, but if not oh well. Just looking for other input and see if anyone else has a better way.
You can use List.IndexOf
var ordered = listOfFoo.OrderBy(o => sortOrderIDs.IndexOf(o.Id));
Edit: Since sortOrderIDs is an array:
var ordered = listOfFoo.OrderBy(o => Array.IndexOf(sortOrderIds, o.Id));
Or, if you want to use the same for lists and arrays, cast it to IList:
var ordered = listOfFoo.OrderBy(o => ((IList)sortOrderIds).IndexOf(o.Id));
You could use something like this:
var ordered = listOfFoo.OrderBy(x => Array.IndexOf(sortOrderIds, x.Id));
This would sort them according to the order of the IDs in sortOrderIds. Foo objects whose IDs are not found will be at the very top of the resulting list.
If you want them to be at the bottom, change the code like this:
var ordered = listOfFoo.OrderBy(x =>
{
var idx = Array.IndexOf(sortOrderIds, x.Id);
return idx == -1 ? int.MaxValue : idx;
});
I have a list stored in resultlist as follows:
var resultlist = results.ToList();
It looks something like this:
ID FirstName LastName
-- --------- --------
1 Bill Smith
2 John Wilson
3 Doug Berg
How do I remove ID 2 from the list?
List<T> has two methods you can use.
RemoveAt(int index) can be used if you know the index of the item. For example:
resultlist.RemoveAt(1);
Or you can use Remove(T item):
var itemToRemove = resultlist.Single(r => r.Id == 2);
resultList.Remove(itemToRemove);
When you are not sure the item really exists you can use SingleOrDefault. SingleOrDefault will return null if there is no item (Single will throw an exception when it can't find the item). Both will throw when there is a duplicate value (two items with the same id).
var itemToRemove = resultlist.SingleOrDefault(r => r.Id == 2);
if (itemToRemove != null)
resultList.Remove(itemToRemove);
Short answer:
Remove (from list results)
results.RemoveAll(r => r.ID == 2); will remove the item with ID 2 in results (in place).
Filter (without removing from original list results):
var filtered = result.Where(f => f.ID != 2); returns all items except the one with ID 2
Detailed answer:
I think .RemoveAll() is very flexible, because you can have a list of item IDs which you want to remove - please regard the following example.
If you have:
class myClass {
public int ID; public string FirstName; public string LastName;
}
and assigned some values to results as follows (used for all examples below):
var results = new List<myClass> {
new myClass { ID=1, FirstName="Bill", LastName="Smith" }, // results[0]
new myClass { ID=2, FirstName="John", LastName="Wilson" }, // results[1]
new myClass { ID=3, FirstName="Doug", LastName="Berg" }, // results[2]
new myClass { ID=4, FirstName="Bill", LastName="Wilson" } // results[3]
};
Then you can define a list of IDs to remove:
var removeList = new List<int>() { 2, 3 };
And simply use this to remove them:
results.RemoveAll(r => removeList.Any(a => a==r.ID));
It will remove the items 2 and 3 and keep the items 1 and 4 - as specified by the removeList. Note that this happens in place, so there is no additional assigment required.
Of course, you can also use it on single items like:
results.RemoveAll(r => r.ID==4);
where it will remove Bill with ID 4 in our example.
A last thing to mention is that lists have an indexer, that is, they can also be accessed like a dynamic array, i.e. results[3] will give you the 4th element in the results list (because the first element has the index 0, the 2nd has index 1 etc).
So if you want to remove all entries where the first name is the same as in the 4th element of the results list, you can simply do it this way:
results.RemoveAll(r => results[3].FirstName == r.FirstName);
Note that afterwards, only John and Doug will remain in the list, Bill is removed (the first and last element in the example). Important is that the list will shrink automatically, so it has only 2 elements left - and hence the largest allowed index after executing RemoveAll in this example is 1 (which is results.Count() - 1).
Some Trivia:You can use this knowledge and create a local function
void myRemove() { var last = results.Count() - 1;
results.RemoveAll(r => results[last].FirstName == r.FirstName); }
What do you think will happen, if you call this function twice?
Like
myRemove(); myRemove();
Answer (click to show):
The first call will remove Bill at the first and last position, the second call will remove Doug and only John Wilson remains in the list.
Note: Since C# Version 8, you can as well write results[^1] instead of var last = results.Count() - 1; and results[last]:
void myRemove() => results.RemoveAll(r => results[^1].FirstName == r.FirstName);
So you would not need the local variable last anymore (see indices and ranges). Furthermore, since it is a one-liner, you don't require the curly braces and can use => instead.
For a list of all the new features in C#, look here.
DotNetFiddle: Run the demo
resultList = results.Where(x=>x.Id != 2).ToList();
There's a little Linq helper I like that's easy to implement and can make queries with "where not" conditions a little easier to read:
public static IEnumerable<T> ExceptWhere<T>(this IEnumerable<T> source, Predicate<T> predicate)
{
return source.Where(x=>!predicate(x));
}
//usage in above situation
resultList = results.ExceptWhere(x=>x.Id == 2).ToList();
You don't specify what kind of list, but the generic List can use either the RemoveAt(index) method, or the Remove(obj) method:
// Remove(obj)
var item = resultList.Single(x => x.Id == 2);
resultList.Remove(item);
// RemoveAt(index)
resultList.RemoveAt(1);
More simplified:
resultList.Remove(resultList.Single(x => x.Id == 2));
there is no needing to create a new var object.
There is another approach. It uses List.FindIndex and List.RemoveAt.
While I would probably use the solution presented by KeithS (just the simple Where/ToList) this approach differs in that it mutates the original list object. This can be a good (or a bad) "feature" depending upon expectations.
In any case, the FindIndex (coupled with a guard) ensures the RemoveAt will be correct if there are gaps in the IDs or the ordering is wrong, etc, and using RemoveAt (vs Remove) avoids a second O(n) search through the list.
Here is a LINQPad snippet:
var list = new List<int> { 1, 3, 2 };
var index = list.FindIndex(i => i == 2); // like Where/Single
if (index >= 0) { // ensure item found
list.RemoveAt(index);
}
list.Dump(); // results -> 1, 3
Happy coding.
Try this code:
resultlist.Remove(resultlist.Find(x => x.ID == 2));
... or just resultlist.RemoveAt(1) if you know exactly the index.
{
class Program
{
public static List<Product> list;
static void Main(string[] args)
{
list = new List<Product>() { new Product() { ProductId=1, Name="Nike 12N0",Brand="Nike",Price=12000,Quantity=50},
new Product() { ProductId =2, Name = "Puma 560K", Brand = "Puma", Price = 120000, Quantity = 55 },
new Product() { ProductId=3, Name="WoodLand V2",Brand="WoodLand",Price=21020,Quantity=25},
new Product() { ProductId=4, Name="Adidas S52",Brand="Adidas",Price=20000,Quantity=35},
new Product() { ProductId=5, Name="Rebook SPEED2O",Brand="Rebook",Price=1200,Quantity=15}};
Console.WriteLine("Enter ProductID to remove");
int uno = Convert.ToInt32(Console.ReadLine());
var itemToRemove = list.Find(r => r.ProductId == uno);
if (itemToRemove != null)
list.Remove(itemToRemove);
Console.WriteLine($"{itemToRemove.ProductId}{itemToRemove.Name}{itemToRemove.Brand}{itemToRemove.Price}{ itemToRemove.Quantity}");
Console.WriteLine("------------sucessfully Removed---------------");
var query2 = from x in list select x;
foreach (var item in query2)
{
/*Console.WriteLine(item.ProductId+" "+item.Name+" "+item.Brand+" "+item.Price+" "+item.Quantity );*/
Console.WriteLine($"{item.ProductId}{item.Name}{item.Brand}{item.Price}{ item.Quantity}");
}
}
}
}
Using LINQ what is the best way to select a single item from a list if the item may not exists in the list?
I have come up with two solutions, neither of which I like. I use a where clause to select the list of items (which I know will only be one), I can then check the count and make a Single call on this list if count is one, the other choice is to use a foreach and just break after getting the item.
Neither of these seem like a good approach, is there a better way?
You can use IEnumerable.First() or IEnumerable.FirstOrDefault().
The difference is that First() will throw if no element is found (or if no element matches the conditions, if you use the conditions). FirstOrDefault() will return default(T) (null if it's a reference type).
Use the FirstOrDefault selector.
var list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var firstEven = list.FirstOrDefault(n => n % 2 == 0);
if (firstEven == 0)
Console.WriteLine("no even number");
else
Console.WriteLine("first even number is {0}", firstEven);
Just pass in a predicate to the First or FirstOrDefault method and it'll happily go round' the list and picks the first match for you.
If there isn't a match, FirstOrDefault will returns the default value of whatever datatype the list items is.
Hope this helps :-)
List<string> items = new List<string>();
items.Find(p => p == "blah");
or
items.Find(p => p.Contains("b"));
but this allows you to define what you are looking for via a match predicate...
I guess if you are talking linqToSql then:
example looking for Account...
DataContext dc = new DataContext();
Account item = dc.Accounts.FirstOrDefault(p => p.id == 5);
If you need to make sure that there is only 1 item (throws exception when more than 1)
DataContext dc = new DataContext();
Account item = dc.Accounts.SingleOrDefault(p => p.id == 5);
Just to complete the answer, If you are using the LINQ syntax, you can just wrap it since it returns an IEnumerable:
(from int x in intList
where x > 5
select x * 2).FirstOrDefault()
Maybe I'm missing something here, but usually calling .SingleOrDefault() is the way to go to return either the single element in the list, or a default value (null for reference or nullable types) if the list is empty.
It generates an exception if the list contains more than one element.
Use FirstOrDefault() to cover the case where you could have more than one.
There are two easy ways, depending on if you want to deal with exceptions or get a default value.
You can use the First<T>() or the FirstOrDefault<T>() extension method to get the first result or default(T).
var list = new List<int> { 1, 2, 4 };
var result = list.Where(i => i == 3).First(); // throws InvalidOperationException
var result = list.Where(i => i == 3).FirstOrDefault(); // = 0
SingleOrDefault() is what you need
cheers
just saw this now, if you are working with a list of object you can try this
public class user
{
public string username { get; set; }
public string password { get; set; }
}
List<user> userlist = new List<user>();
userlist.Add(new user { username = "macbruno", password = "1234" });
userlist.Add(new user { username = "james", password = "5678" });
string myusername = "james";
string mypassword = "23432";
user theUser = userlist.Find(
delegate (user thisuser)
{
return thisuser.username== myusername && thisuser.password == mypassword;
}
);
if (theUser != null)
{
Dosomething();
}
else
{
DoSomethingElse();
}