Assuming I had a collection like this..
var list = new List<Item>
{
new Item
{
Name = "Software",
Price = 100
},
new Item
{
Name = "Software",
Price = 200
},
new Item
{
Name = "Hardware",
Price = 100
}
};
And the 'Names' are not going to be known, I want to write a LINQ query that will return a list of everything with a matching name. I cannot use "Select", because the names, again, are not known at design time.
Any ideas?
I'm not entirely sure whether you want to filter, or group the results.
If you want to filter, you can use Where with a runtime-supplied name:
string nameToFind = GetTheNameToFind(); // Can come from user, config, etc
var matches = list.Where(item => item.Name == nameToFind);
If you want to group by all of the names (ie: have 2 software + 1 hardware element), you could use Enumerable.GroupBy:
var groups = list.GroupBy(item => item.Name);
foreach(var group in groups)
{
Console.WriteLine("Group {0}:", group.Key);
foreach(var item in group)
Console.WriteLine(" Item: {0} - {1}", item.Name, item.Price);
}
Are you looking for this? You can use Where method to filter enumerable.
name variable can be defined at runtime.
string name = "Software";
List<Item> newList = list.Where(item => item.Name == name).ToList();
Related
I have a Dictionary
Dictionary<string, List<Employee>> employees
that contains a list of employees. And I want to allow the user to display the list of employees in alphabetical order based on the state they are in.
Console.WriteLine($"If you would like to sort this list please enter one of the following choices..\n" +
$"'STATE', 'INCOME', 'ID', 'NAME', 'TAX' other wise enter any key.");
var sort = Console.ReadLine().ToUpper();
var employees = EmployeeRecord.employees;
List<Employee> sortedEmps = new List<Employee>();
if (sort.Contains("STATE"))
foreach (var list in employees.Values) {
var columnQuery = list.OrderBy(x => x.stateCode).ToList();
sortedEmps.AddRange(columnQuery);
}
}
//Print out the newly ordered list
foreach (Employee r in sortedEmps) {
Console.WriteLine($"ID: {r.iD} Name: {r.name} State: {r.stateCode} Income:{r.income} Tax Due: {r.taxDue}");
}
However, it still prints out the list without ordering it. How can I get it to order alphabetically by the state code?
Try sorting when you have all data merged.
if (sort.Contains("STATE")) {
foreach (var list in employees.Values) {
sortedEmps.AddRange(list);
}
sortedEmps = sortedEmps.OrderBy(x => x.stateCode).ToList();
}
Also you can shorten a little the code with SelectMany as #Robert Harvey suggested
if (sort.Contains("STATE")) {
sortedEmps = employees.Values
.SelectMany(x => x)
.ToList().OrderBy(o => o.stateCode).ToList();
}
I've a list of records from DB like:
1;Order1;Name1;10,99
2;Order1;Name2;10,99
3;Order1;Name3;10,99
In the view I must list the names separated by comma:
Order1 contains products: Name1, Name2, Name3
Summary: 10,99
Now I take a list of items and I group it by Order1 and then I use for loop where I join names
foreach (var order in orders.GroupBy(f => f.OrderId))
{
var o = order.First();
o.Name = string.Join(", ", order.Select(f => f.Name));
items.Add(o); // this items are being mapped
}
at the end I map this list by automapper. I don't like this solution. Is it somehow possible to move this "name joining" do automapper?
Seems like you need to do the following (i not yet tested)
foreach (var order in orders.GroupBy(f => f.OrderId))
{
var strOrderId = order.Key;
var strNames = string.Join(", ", order.Select(f => f.Name));
Console.Writeline("{0} contains {1} ", strOrderId, strNames);
}
List A: 3,5,5,5,7,9
List B: 3,5
Both of the list are the same type and those values are from a field ID. My objective is to construct a forloop that will return me 7,9 because 7,9 is not existed in List B.
I've tried the following but no luck:
int counter = 0;
foreach(var item in ListA.Where(x=>ListB.Any(b=>x.ID != b.ID)))
{
counter++;
//Here I should perform operation with item that having ID 7 and 9
}
Updates:
Using a except method in the above case, counter will still return me 4 simply because each of the 5 in ListA are different object eventhou they are sharing the same ID. My ultimate objective is to have the counter as 2 irregardless whether the object is the same or not. As long as the ID of object in ListA is 3 or 5, I would wanna exclude it.
Just use the Except extension mtehod
foreach (var item in ListA.Except(ListB)) {
...
}
it should be "ALL", or "Not Any"
foreach(var item in ListA.Where(x=>ListB.All(b=>x.ID != b.ID)))
{
//Here I should perform operation with item that having ID 7 and 9
}
update:
As you actually want to have distinct result from A except B, so, you can do either:
foreach(var item in ListA.GroupBy(m=>m.ID).Where(x=>ListB.All(b=>b.ID != x.Key)))
{
counter ++;
Debug.writeline(item.Key);
}
or
foreach(var id in ListA.Select(x=>x.ID).Distinct().Except(ListB.Select(y=>y.ID)))
{
counter++;
}
note: all untested - i have no compiler with me for the moment.
Change your query like this:
foreach(var item in ListA.Where(x=> !ListB.Any(b => x.ID == b.ID)))
And it should work fine.
Try This:
List<int> listA=new List<int>(new[]{ 3,5,7,9});
List<int> listB=new List<int>(new[]{ 3,5});
var items=(from a in listA
select a).Except(from b in listB
select b);
foreach(var item in items)
{
Console.WriteLine(ll);
}
Output:
7
9
Except method can be used when both List are of same type.
If Type is different. We can use like this.
var outPut = _employees.Where(i => _employeeExtensions.Any(j => i.EmpId == j.EmpId));
I think you want to get the items in a list where the items' IDs are different:
Example that I put together in LinqPad:
void Main()
{
List<Person> a = new List<Person>()
{
new Person { ID = 1 },
new Person { ID = 2 },
new Person { ID = 3 },
};
List<Person> b = new List<Person>()
{
new Person { ID = 1 },
};
var c = a.Where(x => b.Any(bprime => bprime.ID != x.ID));
foreach(var item in c)
{
Console.WriteLine(item.ID);
}
}
class Person
{
public int ID { get; set; }
}
Output:
2
3
This works similar to the Except method but this will check the elements' properties.
I have two list say
List<string> names; and List<Student> stud;
Student Class has 3 properties
ID
Name
Section
Now i want to loop through List<string> and compare each item with Name property in List<Student> and want to perform operations if they are not equal
I tried looping through names and comparing each values to stud.
But i thought there must be some better way of doing this with LINQ or should i be using YIELD.
Thanks
It's not very clear from your description, but if you want "all students whose names aren't in the list" you can definitely use LINQ:
var studentsWithoutListedNames = stud.Where(s => !names.Contains(s.Name));
foreach (var student in studentsWithoutListedNames)
{
// Whatever...
}
If your intention is not what Jon describes but more to compare the list of names with the list of student names and find differences:
var invalidStudents = names.Zip(stud, (name, student) => new {name, student}).
Where(item => (item.name != item.student.Name));
if (invalidStudents.Any()) // Or foreach...
{
...
}
for example:
var names = new string[] { "John", "Mary" };
var stud = new Student[] { new Student(1, "John", "IT"), new Student(2, "Jack", "Math") };
var invalidStudents = names.Zip(stud, (name, student) => new {name, student}).
Where(item => (item.name != item.student.Name));
foreach (var item in invalidStudents)
{
Console.WriteLine(item.name);
}
Should write Mary
Another good way of doing this would be:
var notOnList = students.Except(from student in students
join name in names on student.Name equals name
select student);
foreach(var student in notOnList)
{
...
}
I'm new to Linq. I want to know whether this is the best way or are there any other ways to do this.
I have a requirement where from a web service, I receive a list of items:
class Item {
string ItemName { get; set;}
string GroupName { get; set; }
}
I receive the following data:
ItemName: Item1; GroupName: A
ItemName: Item2; GroupName: B
ItemName: Item3; GroupName: B
ItemName: Item4; GroupName: A
ItemName: Item5; GroupName: A
Now I want to get all of the unique Groups in the list, and associate all the Items to that Group. So I made a class:
class Group {
string GroupName { get; set; }
List<string> Items { get; set; }
}
So that there is a single group and all associated Items will be under the List.
I made two LINQ statements:
var uniqueGroups = (from g in webservice
where g.Group != null
select g.GroupName).Distinct();
Then I loop through it
foreach (var gn in uniqueGroups)
{
var itemsAssociated = (from item in webservice
where item.GroupName = gn.ToString()
select new {
});
}
and then I got the items, and save them to my object.
Is this the best way to do this or are there any LINQ statement that can do all these in one go?
Thanks.
Sounds like you want GroupBy
var itemsByGroup = items.GroupBy(i => i.GroupName);
foreach (var group in itemsByGroup)
{
var groupName = group.Key;
var itemsForThisGroup = group;
foreach (var item in itemsForThisGroup)
{
Console.Out.WriteLine(item.ItemName);
}
}
You can try this:
//List<Item> webservice = list with items from your webservice
var result = (from i in items
group i by i.GroupName into groups
select new Group()
{
GroupName = groups.Key,
Items = groups.Select(g => g.ItemName).ToList()
}).ToList();
I would use:
webservice.ToLookup(k => k.GroupName);
That would eliminate the need for the extra class.
Hope this helps!
That could be done all at once with an anonymous type and Enumerable.GroupBy:
var groupItems =
webservice.Where(i => i.GroupName != null)
.GroupBy(i => i.GroupName)
.Select(grp => new { Group = grp.Key, Items = grp.ToList() });
foreach (var groupItem in groupItems)
Console.WriteLine("Groupname: {0} Items: {1}"
, groupItem.Group
, string.Join(",", groupItem.Items.Select(i => i.ItemName)));
Distinct is useless since GroupBy will always make the groups distinct, that's the nature of a group.
Here's running code: http://ideone.com/R3jjZ