I'm new at C#, I know how to do a LINQ search to a List with one field/type, but not with many types of an object. I created a List
List<Reader> results = new List<Reader>();
That contain this class:
public class Reader
{
public int ID { get; set; }
public string Name { get; set; }
public string Course { get; set; }
public int Grade { get; set; }
public Reader(int id, string name, string course, int grade)
{
ID = id;
Name = name;
Course = course;
Grade = grade;
}
}
I want to search it with LINQ and match the ID and Name of a user that entered the site.
If this two fields are the same I want to take from the List the users Course and Grade.
Any suggestion how to do it ?
A simple Where for condition(s) and Select for representation should do:
List<Reader> results = ...
var data = results
.Where(item => item.ID == userID && item.Name == userName)
// .OrderBy(item => item.Course) // uncomment if you want to order by course
.Select(item => $"Course: {item.Course} Grade: {item.Grade}");
foreach (var record in data)
Console.WriteLine(record);
First, let's assume that you have two variables that hold the values introduced by the user. Those variables are userName of type string and id of type integer. If you just want a variable that holds the course and the Grade you could select a new anonymous type and do the query like this:
var values= results
.Where(item => item.ID == userID && item.Name == userName)
.Select(item => new { Course = item.Course, Grade = item.Grade });
then you could use the values like:
values.Grades
values.Course
var Selecteduser = results.Where(x => x.Name == selectedname && x.ID == ID).ToList();
if (Selecteduser.Count != 0)
{
//found match ..(Selecteduser[0])
string selectedcourse = Selecteduser[0].Course;
int selectedgrade = Selecteduser[0].Grade;
}
else
{
//coudlnt find match
}
Related
I am trying to solve the following LINQ problem: to get the correct index of an element in a collection of objects (if element is found) or return the next index that should be assigned to it (if not found).
My scenario is a bit more complex than that one: https://coderwall.com/p/iqrkuq/get-the-index-of-an-element-in-a-collection-via-linq since the class I am dealing with is as follows:
public class PersonIdentifier
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? BirthDateDate { get; set; }
public string PersonID { get; set; } //this field is a concatenation of FirstName + LastName + **the index associated to the birthdate**.
}
I can group by or split using any of the 3 first properties. Each of them depends on a boolean condition:
bool SplitByFirstName; //value can be true or false
bool SplitByLastName; //value can be true or false
bool SplitByBirthDate = true; //value is always true because the **purpose of the method is to get the correct index associated to a given birth date.**
My collection is a list as follows:
private static IList<PersonIdentifier> PersonList =
new List<PersonIdentifier>()
{
new PersonIdentifier() { FirstName = "John", LastName = "DOE", BirthDateDate = new DateTime(2001, 1, 1), PersonID = "JohnDOE1" },
};
Actually, I tried this:
private static int GenerateIndice(bool splitByFirstName, bool splitByLastName, string relatedFirstName, string relatedLastName, DateTime? birthDate)
{
if (PersonList.Any())
{
if (splitByFirstName == true && !string.IsNullOrEmpty(relatedFirstName))
{
//TODO
}
if (splitByLastName == true && !string.IsNullOrEmpty(relatedLastName))
{
//TODO
}
//remember that SplitbyBirthDate is always true.
if (birthDate != null)
{
var results = PersonList.GroupBy(p => new { p.BirthDate });
int indice = results.Where(ex => ex.Key.BirthDate.HasValue && ex.Key.BirthDate == birthDate).Count();
if (indice == 0)
{
return results.Distinct().Count() + 1;
}
else
{
return results.Select((ex, i) => new
{
Item = ex,
Position = i
}).Where(m => m.Item.Key.BirthDate == birthDate).First().Position + 1;
}
}
}
return 1;
}
This seems to always return the correct index associated to the birthdate. However, the code should work for any combination of the boolean values:
combination of the 3 boolean possible values
When adding another John DOE (with a different birth date), the index returned should be 2 in this scenario as we already have 1 John DOE in the list.
I need your help please. Thanks guys and sorry for the english as i am a french speaker.
Some scenarios to explain the usage of the bools
usage of the bools
It's not clear to me what these splitByFirst/Last/Birthdate bools are trying to achieve. I recommend looking into the Select Overload which will project an index along with your the person object in your list. Select Overload
[edit]
You're not really finding the "index" of an item in you collection, you're finding the count of a items with similar properties. At some point you need to decide which properties you'd like to be in common. you can do this with a series of lambda functions, or by crafting a concatenated (not split) string, but at some point you need to make the determination of what properties you want to be in common.
this is what i came up with.
void Main()
{
var people = new List<Person>();
var personOne = new Person{ FirstName = "jeff", LastName = "my name is", BirthDate = DateTime.Today};
people.Add(personOne);
Console.WriteLine(AddPerson(personOne, people, GroupCriteria.Birthday));
Console.WriteLine(AddPerson(personOne, people, GroupCriteria.BirthdayFirstName));
Console.WriteLine(AddPerson(personOne, people, GroupCriteria.BirthdayFirstNameLastName));
Console.WriteLine(AddPerson(personOne, people, GroupCriteria.BirthdayLastName));
var personTwo = new Person{ FirstName = "not jeff", LastName = "my name is", BirthDate = DateTime.Today};
people.Add(personTwo);
Console.WriteLine(AddPerson(personTwo, people, GroupCriteria.Birthday));
Console.WriteLine(AddPerson(personTwo, people, GroupCriteria.BirthdayFirstName));
Console.WriteLine(AddPerson(personTwo, people, GroupCriteria.BirthdayFirstNameLastName));
Console.WriteLine(AddPerson(personTwo, people, GroupCriteria.BirthdayLastName));
}
int AddPerson(Person newPerson, List<Person> people, GroupCriteria crit)
{
Func<Person, bool> countFunc = p => p.BirthDate == newPerson.BirthDate; ;
switch(crit)
{
case GroupCriteria.Birthday:
countFunc = p => p.BirthDate == newPerson.BirthDate;
break;
case GroupCriteria.BirthdayFirstName:
countFunc = p => p.BirthDate == newPerson.BirthDate && p.FirstName == newPerson.FirstName;
break;
case GroupCriteria.BirthdayLastName:
countFunc = p => p.BirthDate == newPerson.BirthDate && p.LastName == newPerson.LastName;
break;
case GroupCriteria.BirthdayFirstNameLastName:
countFunc = p => p.BirthDate == newPerson.BirthDate && p.FirstName == newPerson.FirstName && p.LastName == newPerson.LastName;
break;
}
var index = people.Count(countFunc);
if(index == 0)
return people.Count + 1;
return index;
}
public class Person
{
public string FirstName {get; set;}
public string LastName {get; set;}
public DateTime BirthDate {get;set;}
}
public enum GroupCriteria
{
Birthday,
BirthdayFirstName,
BirthdayLastName,
BirthdayFirstNameLastName
}
It seems like a silly exercise. that doesn't have a ton to do with linq. Would love to see someone smarter than me comes up with.
I want to return a list of active groups that are discounted in requested states. The list of groups each have a list of states which include the state abbrev and a discount flag.
filter criteria:
string[] states //list of state abbreviations
List to filter:
public class WorksiteGroup
{
public long Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public bool IsDiscontinued { get; set; }
public List<WorksiteGroupState> ActiveStates { get; set; } = new List<WorksiteGroupState>();
}
public class WorksiteGroupState
{
public string StateAbbrev { get; set; }
public bool IsDiscountApplied { get; set; }
}
Again, I want to return a list of WorksiteGroup with the full structure above where IsDiscontinued is false and have an ActiveState where StateAbbrev matches any of the filter criteria (states[]) and IsDiscountApplied is true for that state.
Let's do this step by step and then we can merge operations where necessary.
I want to return a list of WorksiteGroup with the full structure above
where IsDiscontinued is false
source.Where(e => !e.IsDiscontinued);
and have an ActiveState where StateAbbrev matches any of the filter
criteria (states[])
now let's take the previous pipeline and chain this criterion into it.
source.Where(e => !e.IsDiscontinued)
.Where(e => e.ActiveStates.Any(a => states.Contains(a.StateAbbrev)))
and IsDiscountApplied is true for that state.
source.Where(e => !e.IsDiscontinued)
.Where(e => e.ActiveStates.Any(s => states.Contains(s.StateAbbrev) && s.IsDiscountApplied));
for efficiency let's swap the Contains call to be after s.IsDiscountApplied e.g.
source.Where(e => !e.IsDiscontinued)
.Where(e => e.ActiveStates.Any(s => s.IsDiscountApplied && states.Contains(s.StateAbbrev)));
You can try this using Linq:
string[] states = new string[] { "abbrev1", "abbrev2" };
var list = new List<WorksiteGroup>();
var item = new WorksiteGroup();
item.Name = "Test1";
item.IsDiscontinued = false;
var subitem = new WorksiteGroupState();
subitem.IsDiscountApplied = true;
subitem.StateAbbrev = "abbrev1";
item.ActiveStates.Add(subitem);
list.Add(item);
item = new WorksiteGroup();
item.Name = "Test2";
item.IsDiscontinued = true;
subitem = new WorksiteGroupState();
subitem.IsDiscountApplied = true;
subitem.StateAbbrev = "abbrev1";
item.ActiveStates.Add(subitem);
list.Add(item);
var result = list.Where(wg => wg.IsDiscontinued == false
&& wg.ActiveStates.Where(state => state.IsDiscountApplied == true
&& states.Contains(state.StateAbbrev)).Any());
foreach ( var value in result )
Console.WriteLine(value.Name);
Console.ReadKey();
You can play with items and add more to see results.
sudo-code but would something like below work, im sure you could do this is one line but
var worksiteGroup = Populate();
var filteredWorkSiteGroup = worksiteGroup .Where(x=>x.IsDiscontinued == false);
filteredWorkSiteGroup.ActiveStates = filteredWorkSiteGroup.ActiveStates
.Where(x=> states.Contains(x.StateAbbrev)
&& x.IsDiscountApplied == true);
I have 2 tables Orders and Items and I am trying to query these 2 tables for statistics retrieval.
Orders has columns OrderID[PK], ItemID[FK], OrderStatus etc.
Items has columns ItemID[PK], ItemName, ItemPrice etc.
I am fetching list of orders based on date range and then I am returning their counts based on their status.
Below is my StatisticsResponse.cs to return the response.
public class StatisticsResponse
{
public int CancelledOrderCount { get; set; }
public int CompletedOrderCount { get; set; }
public int InProgressOrderCount { get; set; }
public int TotalOrders { get; set; }
public Dictionary<string,int> ItemOrders { get; set;}
}
This is how I am retrieving Orders between 2 dates.
var orders = _unitOfWork.OrderRepository
.GetMany(x => (x.OrderStatus == "Pending"
&& x.OrderDate.Value.Date >= dtStartDate
&& x.OrderDate.Value.Date < dtEndDate) ||
((x.OrderStatus == "Completed" || x.OrderStatus == "Cancelled")
&& x.DeliveryDate.Date >= dtStartDate || x.DeliveryDate.Date < dtEndDate) || (x.LastUpdated.Value.Date >= dtStartDate || x.LastUpdated.Value.Date < dtEndDate)).ToList();
if (orders != null)
{
return new StatisticsResponse()
{
TotalOrders = orders.Count(),
CancelledOrderCount = orders.Where(x => x.OrderStatus == "Cancelled").Count(),
CompletedOrderCount = orders.Where(x => x.OrderStatus == "Completed").Count(),
InProgressOrderCount = orders.Where(x => x.OrderStatus != "Completed" && x.OrderStatus != "Cancelled").Count()
}
}
Now, in the ItemOrders property, which is of type Dictionary<string,int>, I want to group each item with their name and count. I have ItemID in my orders list, and I would like to join 2 tables to get the name before storing.
I have tried to use GroupBy as below but am totally stuck on how to get the name for the Item after grouping
ItemOrders = new Dictionary<string, int>
{
orders.GroupBy(x=>x.ItemID)// Stuck here
}
I also read about GroupJoin but couldn't quite make sure whether it can fit in here.
Could someone please let me know how I can join these 2 tables to get their name based on their ID?
You can use something along this:
using System.Entity.Data; //to use Include()
...
Dictionary<string,int> itemOrders = dbContext.Orders.Include(o=> o.Item)
.GroupBy(o=> o.Item)
.ToDictionary(g => g.Key.Name, g => g.Count());
This is assuming:
There is a navigation property set up from Order to Item
Each Order has one Item
So, I was able to achieve this with GroupJoin through various online example as below:
if (orders != null)
{
var items = _unitOfWork.ItemRepository.GetAll();
var itemOrders = items.GroupJoin(orders,
item => item.ItemID,
ord => ord.ItemID,
(item, ordrs) => new
{
Orders = ordrs,
itemName = item.ItemName
});
StatisticsResponse statsResponse = new StatisticsResponse()
{
//...
};
foreach (var item in itemOrders)
{
statsResponse.ItemOrders.Add(item.itemName, item.Orders.Count());
}
return statsResponse;
}
I have the following class
public class SolicitacaoConhecimentoTransporte
{
public long ID { get; set; }
public string CodigoOriginal { get; set; }
public DateTime Data { get; set; }
public List<CaixaConhecimentoTransporte> Caixas { get; set; }
}
I would like to know if there is a way of achiveing the same behavior of the code below using Linq (with lambda expression syntax),
List<SolicitacaoConhecimentoTransporte> auxList = new List<SolicitacaoConhecimentoTransporte>();
foreach (SolicitacaoConhecimentoTransporte s in listaSolicitacao)
{
SolicitacaoConhecimentoTransporte existing =
auxList.FirstOrDefault(f => f.CodigoOriginal == s.CodigoOriginal &&
f.Data == s.Data &&
f.ID == s.ID);
if (existing == null)
{
auxList.Add(s);
}
else
{
existing.Caixas.AddRange(s.Caixas);
}
}
return auxList;
In other words, group all entities that have equal properties and flat all lists into one.
Thanks in advance.
Use anonymous object to group by three properties. Then project each group to new SolicitacaoConhecimentoTransporte instance. Use Enumerable.SelectMany to get flattened sequence of CaixaConhecimentoTransporte from each group:
listaSolicitacao.GroupBy(s => new { s.CodigoOriginal, s.Data, s.ID })
.Select(g => new SolicitacaoConhecimentoTransporte {
ID = g.Key.ID,
Data = g.Key.Data,
CodigoOriginal = g.Key.CodigoOriginal,
Caixas = g.SelectMany(s => s.Caixas).ToList()
}).ToList()
I have the following:
IEnumerable<Corporations> Corps = ...
public class Corporation
{
public string CompanyName { get; set; }
public virtual ICollection<Cars> Cars { get; set; }
}
public class Car
{
public int CarID { get; set; }
public string CarName { get; set; }
}
If I have the CarID = 4, how do I get CarName and Company name using linq.
I think I have come close with the .foreach (which is old school?) but get lost closing it:
var result = Corps.Where(cps => cps.Cars != null).ToList()
.ForEach(x => x.Cars.ToList()
.ForEach(cr => cr.CarID == 5)
.Select (#y => new { FoundCorp = x.CompanyName, FoundCar = cr.CarName}....
The code below returns anonymous class instances, with property Cars containing car item matching conditon and Corps property for this car found.
var results = Corps
.SelectMany(corp => corp.Cars, (corps, cars) => new { Corps = corps, Cars = cars })
.Where(item => item.Cars.CarID == 4)
;
foreach (var item in results)
{
Console.WriteLine("Car with id {0} have found in for company {1}", item.Cars.CarID, item.Corps.CompanyName);
}
You might want to replace this anonymous class with some particular that holds only CarName and CompanyName. Just replace the instantiation as below:
var results = Corps
.SelectMany(corp => corp.Cars, (corps, cars) => new SomeResult(corps.CompanyName, cars.CarName))
.Where(item => item.Cars.CarID == 4)
;
This code is safe to number of cars/companies found.
var check = Corporation.SelectMany(p => p.Cars.Where(m => m.CarID == "4").Select(n => new
{
n.CarName,
p.CompanyName
}));
you want to find the corp where any car matches your CarId =>
int carid = 1;
var corp = Corps.FirstOrDefault(corp => corp.Cars.Any(car => car.CarID == carid));
if (Corp != null)
{
Console.WriteLine(Corp.CompanyName);
Console.WriteLine(Corp.Cars.First(car => car.CarID == carid).CarName);
}
As per Smudge's notes, you may consider using a HashTable or similar if you want to ensure your CarId's are unique.