Select single item from a list - c#

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

Related

Get Random item from table without repetition

I am creating an application where I have to display a question from a list without repetition.
public IEnumerable<dynamic> GetQue()
{
var result = obj.tblQuestions
.OrderBy(r => Guid.NewGuid())
.Select(o => new { o.id, o.Question, o.Opt1, o.Opt2, o.Opt3, o.Opt4 })
.Take(1);
return result;
}
Currently I am getting a random question but with repetition. How do I get a record without repetition?
As I said in the comment, you can get elements one by one, using a random, and then remove the selected elements from list. Repeat this until the list is empty.
I am not gving yu exactly the code necessary for your case, you will still need to adapt it to your classes, but this is the principle it shoud respect:
var list = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int randomId;
Random rand = new Random();
if (list.Count != 0)
{
randomId = rand.Next(list.Count);
var randomElement = list[randomId];
list.RemoveAt(randomId);
return randomElement;
}
This gets the random elements from a list of integers, considering your list is the data iside a class, not the one you should renew, of course.
public ActionResult GetNextQuestion(int[] prevs = null)
{
var que = GetQue(prevs);
var ids = new int[] { que.id};
if(prevs != null)
ids = ids.Concat(prevs);
ViewBag.list = ids;
return View(que);
}
public dynamic GetQue(int[] prevs = null)
{
using (var obj = new Db())
{
var result = obj.tblQuestions;
if(prevs != null)
result = result.Where(e => !prevs.Contains(e.id));
result = result.OrderBy(r => new Guid())
.Select(o => new { o.id, o.Question, o.Opt1, o.Opt2, o.Opt3, o.Opt4 });
return result.First();
}
}
Source:how to avoid number repeation by using random class in c#?
If you add the items to a list as you cycle them, you can check the list to see if its been added or not. I'm pretty rookie, so i cant really code it out for you, but the idea is there. Make a seperate list for the entries you've already cycled through, then do maybe an if statement to check if the next entry is in the list before executing it.
I would have done this in a comment, but i dont have 50 rep, so i cant start a comment chain. :/

How to return all items in an ObservableCollection which satisfy a condition C#

I'm trying to find a neat way to find all of the values in an observable collection which meet a certain criteria. For this example to keep things simple lets say its the collection contains ints and I'm trying to find all of the items that are greater than 5.
The best way I currently know of doing it is like this
ObservableCollection<Int> findAllGreaterThanFive (ObservableCollection<Int> numbers)
{
ObservableCollection<Int> numbersGreaterThanFive;
foreach(Int number in numbers)
{
if (number > 5)
{
numbersGreaterThanFive.add(number);
}
}
return numbersGreaterThanFive;
}
Obviously ignore any simple solutions that take advantage to the fact I'm looking for ints I need a solution that works with any an ObservableCollection of any type with any condition. I was just wondering if checking every item with the foreach loop and the conditional is the best way of doing it?
You can say something like:
var numbersGreaterThanFive = numbers.Where(x => x > 5);
you can use System.Linq namespace, add using statement using System.Linq and after that you can use following Where method.
ObservableCollection<int> list = new ObservableCollection<int>();
list.Where(i => i > 5).ToList();
you can use any kind of objects like :
ObservableCollection<DataItem> list = new ObservableCollection<DataItem>();
list.Where(i => i.ID > 10);
The code above returns DataItem's with ID greater than 10.
If you sure about there's only one record satisfying condition, you can use First() method like :
ObservableCollection<DataItem> list = new ObservableCollection<DataItem>();
list.First(i => i.ID == 10);
Above code returns the DataItem with ID 10. But if there's no record with ID = 10 then it will throw an exception. Avoid of this if you're not sure there's only one record satisfies the condition. Also you can use FirstOrDefault() method.
ObservableCollection<DataItem> list = new ObservableCollection<DataItem>();
DataItem item = list.FirstOrDefault(i => i.ID == 10);
if(item != null)
{
//DoWork
}
If there's no record with ID = 10, then item will be null.

How to perform LINQ query over Enum?

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)

Create a new anonymous type list to union onto existing anonymous type list

I have a list of anonymous types that I get from my database:
var takenChannels = (from b in bq.GetStuff(db)
where b.RecordType == "H" && b.TourStartDateTime.Date == date
select new { Start = b.TourStartDateTime, End = b.TourEndDateTime, Channel = b.RadioChannel, TourArea = b.TourArea }).ToList();
Then I use this list info to do some stuff in a foreach loop. I want to add to this list a new anonymous item for when I come back round in the loop.
Something like:
takenChannels.Union{new[] { new{Start = DateTime.Now, End = DateTime.Now.AddDays(1), Channel = 25, TourArea = "Area" }});
Obviously this doesn't work. How do I do it?
Edit 1:
takenChannels.Add(new { Start = s, End = e, Channel = channel, TourArea = booking.TourArea });
This is the closest I've got so far (Thanks to Daniel)... but the error I get is:
Error 6 Argument 1: cannot convert from 'AnonymousType#2' to 'AnonymousType#1'
This answer might be a bit late, but since this is the question I found when Googling for the same problem, I think I should complete it with a working answer.
There is no problem to Union multiple times over anonymous types. It is important that all properties are declared in all instances and that they have the same data type. if not, you get the error above.
In your specific case, does the database perhaps return TourStartDateTime or TourEndDateTime as DateTime??
Is RadioChannel an int from the database or perhaps an int? or string?
Is TourArea a string in the database?
Just make sure the data types match and you should be fine. Below is a working snippet of code I use in my own program:
var regions = (
new[] { new { Id = "-1", Name = "---", Pattern = (string)null } }
).Union(
from x in db.Userlists where x.ListType == 2 select new { Id = x.UserlistID.ToString(), Name = x.Name, Pattern = (string)null }
).Union(
from x in db.Lookups where x.Category == "Stock" select new { Id = x.Key, Name = x.Key, Pattern = x.Value }
).ToArray();
You can simply Add to the list:
takenChannels.Add(new { Start = ... });

How to remove item from list in C#?

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

Categories