Linq Query for most occuring property of a class - c#

I know this is really basic but how do I implement a linq query that returns the most occuring field?
This is what I got so far:
var commonAge = from c in customers.GroupBy(s=>s.age)
.OrderByDescending(sg=>sg.Count())
.Take(1)
select s.Key;

Based on your comment, you are looking for the most common Age on a customer data type which has said property.
// Start with the customers collection
var mostCommonAge = customers
// then group by their age,
.GroupBy(c => c.Age,
// and project into a new anonymous type
(key, g) => new {Age = key, Count = g.Count()})
// order by count of each age
.OrderByDescending(g => g.Count)
// and take the first
.First();
Here's a complete working example. With a data model class Customer:
class Customer {
public string Name { get; set; }
public int Age { get;set; }
}
Then you can output the most common age by
static void Main(string[] args) {
var customers = new List<Customer> {
new Customer { Age = 23 },
new Customer { Age = 23 },
new Customer { Age = 23 },
new Customer { Age = 24 },
new Customer { Age = 25 }
};
var mostCommonAge = customers
.GroupBy(c => c.Age,
(key, g) => new {Age = key, Count = g.Count()})
.OrderByDescending(g => g.Count)
.First();
Console.WriteLine(mostCommonAge);
}

Related

group by and merge some field result in linq

I have a class like
public class Person
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
Now I have a list of this class: List<Person> persons;
var persons = new List<Person> {
new Person { Id = 1, LastName = "Reza", FirstName="Jenabi" },
new Person { Id = 1, LastName = "Amin", FirstName="Golmahalle"},
new Person { Id = 2, LastName = "Hamed", FirstName="Naeemaei"}
};
Is there a way I can group by Id and get the list of all the full Name (Combine first and last names)?
So after grouping:
var Id = results[0].Id; // Output : 1
List<string> fullNames = results[0].FullNames; // Output : "Reza Jenabi","Amin Golmahalle"
I believe this is what you need:
var results = persons.GroupBy(x => x.Id)
.Select(x => new { Id = x.Key, FullNames = x.Select(p => $"{p.FirstName} {p.LastName}").ToList() })
.ToList();
I think bellow code can help you:
var fff = from p in persons
group $"{p.FirstName} {p.LastName}" by p.Id into g
select new { PersonId = g.Key, FullNames = g.ToList() };
yeah, you can use GroupBy and Join those items:
var grouped = persons.GroupBy(p => p.Id)
.Select(s => string.Join(", ", s.Select(a=> $"{a.FirstName} {a.LastName}")));

Find unique value in group of groups

Guess i have two groups
using System.Collections.Generic;
namespace ConsoleApplication
{
internal class Student
{
public string Name;
public string Surname;
public int Mark;
}
internal class Program
{
private static void Main()
{
var twoGroups = new List<List<Student>>
{
new List<Student>
{
new Student { Name = "Anna", Surname = "Mors", Mark = 4 },
new Student { Name = "Jimmy", Surname = "Lemon", Mark = 4 }
},
new List<Student>
{
new Student { Name = "Anna", Surname = "Mors", Mark = 4 },
new Student { Name = "Tommy", Surname = "Wojski", Mark = 4 },
new Student { Name = "Jimmy", Surname = "Lemon", Mark = 4 }
}
};
}
}
}
How can i get unique persons from both groups (by Name or any other property) with linq?
It's obvious there is Tommy, but i dont know how can i pick him with linq (or pick a list of unique persons.
UPD
Also, can i pick group contain only unique student.
using System.Collections.Generic;
namespace ConsoleApplication
{
internal class GroupOfStudents
{
public string Code;
public List<Student> Students;
}
internal class Student
{
public string Name;
public string Surname;
public int Mark;
}
internal class Program
{
private static void Main()
{
var twoGroups = new List<GroupOfStudents>
{
new GroupOfStudents
{
Code = "A1",
Students = new List<Student>
{
new Student { Name = "Anna", Surname = "Mors", Mark = 4 },
new Student { Name = "Jimmy", Surname = "Lemon", Mark = 4 }
}
},
new GroupOfStudents()
{
Code = "B2",
Students = new List<Student>
{
new Student { Name = "Anna", Surname = "Mors", Mark = 4 },
new Student { Name = "Tommy", Surname = "Wojski", Mark = 4 },
new Student { Name = "Jimmy", Surname = "Lemon", Mark = 4 }
}
}
};
}
}
}
I think i need to pick student and push him to new Group with same name, right?
flaten the nested lists into one SelectMany()
group by unique criterias GroupBy() (I choose all properties)
select unique records Where()
code:
List<Student> result = twoGroups.SelectMany(x => x)
.GroupBy(x => new { x.Name, x.Surname, x.Mark })
.Where(x => x.Count() == 1)
.Select(x => x.First())
.ToList();
You can use SelectMany, GroupBy and Count
IEnumerable<Student> uniqueStudents = twoGroups
.SelectMany(list => list)
.GroupBy(student => student.Name)
.Where(g => g.Count() == 1)
.Select(g => g.First());
This selects only students which aren't contained multiple times in one of both(or both) lists.
If you want to group-by multiple properties use an anonymous type
.GroupBy(student => new { student.Name, student.Surname })
If you want to allow duplicates in the same list but not across multiple lists:
IEnumerable<Student> uniqueStudents = twoGroups
.SelectMany((list, index) => list.Select(student => new { student, index }))
.GroupBy(x => x.student.Name)
.Where(g =>
{
int index = g.First().index;
return g.Skip(1).All(x => x.index == index);
})
.Select(g => g.First().student);
Acc. your Edit
If you want to remove all students from the lists which appear also in other groups:
var studentNameLookup = twoGroups.SelectMany(g => g.Students).ToLookup(s => s.Name);
twoGroups.ForEach(g => g.Students.RemoveAll(s => studentNameLookup[s.Name].Count() > 1));

Linq expression to fill model containing other List<T>

I have a model (sample below), and sample data with desired output..
I have also given two ways to populate model which will give desired output but for somereason,
looks like I am missing something....
Not able to figure out what is the issue here...
need inputs in correcting approach 1 or approach 2 or you can also suggest any other approach which will help populate model in below response..
models
class Emp
{
public int id {get;set;}
public int Name {get;set;}
public List<cardType> cardTypes {get;set;}
}
class cardType
{
public int name {get;set;}
public DateTime Expiry {get;set;}
}
sample data (Data is returned in 1 table only)
id Name cardTypeName exp
1 a Amex 1010
1 a City 2010
desired output
<Emp>
<ID>1</id>
<name>1</name>
<cardTypes>
<cardType>
<Name> Amex </Name>
<exp> 1010 </exp>
</cardType>
<cardType>
<Name> City </Name>
<exp> 2010 </exp>
</cardType>
<cardTypes>
</Emp>
approach 1
(dataTable.AsEnumerable()
.GroupBy(r => r.ItemArray[1])
.Select(grp => new Emp()
{
id = r.itemarray[1],
name = r.itemarray[1],
cardTypes = grp.Select(t => new cardType()
{
field 1,
field 2
}).ToList()
}));
approach 2
return (from DataRow dr in dataTable.Rows
select new Emp
{
id = "",
name= "",
cardTypes = dataTable.AsEnumerable()
.Select(x => new cardType
{
name = ""
exp = ""
}).ToList(),
});
try this
return dataTable.AsEnumerable()
.GroupBy(x => x.Id)
.Select(x => new
{
id = x.Key,
name = x.Key,
cardTypes = x.Select(t => new
{
name = t.Name,
exp = t.Exp
})
});
This should work for you considering you will change the Expiry field in cardType to int
var results =
dt.AsEnumerable()
.GroupBy(row => row.ItemArray[0])
.Select(rows => new Emp
{
id = rows.Key,
name = rows.Key,
cardTypes = rows.Select(row => new cardType {Name = row.ItemArray[2], Expiry = row.ItemArray[3]})
});
If you desire to have DateTime, then decide how to convert. For example:
cardTypes = rows.Select(row => new cardType {Name = row.ItemArray[2], Expiry = new DateTime(row.ItemArray[3],1,1)})
If you want to serialize to xml your models, you should mark your models with [Serializable] attribute and just use XmlSerializer.
[Serializable]
class Emp
{
public int id {get;set;}
public int Name {get;set;}
public List<cardType> cardTypes {get;set;}
}
[Serializable]
class cardType
{
public int name {get;set;}
public DateTime Expiry {get;set;}
}
After that you can use following code:
static Dictionary<int, Emp> ConvertToDict(DataTable dt)
{
Dictionary<int, Emp> emps = new Dictionary<int, Emp>();
foreach (var dr in dt.AsEnumerable())
{
var id = (int)dr["ID"];
var name = (string)dr["Name"];
var cardTypeName = (string)dr["CardTypeName"];
var exp = (int)dr["Exp"];
Emp emp;
var cardType = new CardType { Name = cardTypeName, Exp = exp };
if (emps.TryGetValue(id, out emp))
{
emp.CardTypes.Add(cardType);
}
else
{
emps.Add(id, new Emp { ID = id, Name = name, CardTypes = new List<CardType> { cardType } });
}
}
return emps;
}
static List<string> Serialize<T>(IEnumerable<T> entities) where T:new()
{
var ser = new XmlSerializer(typeof(T));
var serializedEntities = entities.Select(entity =>
{
using (var sw = new StringWriter())
{
ser.Serialize(sw, entity);
return sw.ToString();
}
}).ToList();
return serializedEntities;
}

Need help querying through 3 classes

I have 3 classes that are defined like this:
class Customer
{
public string Name;
public string City;
public Order[] Orders;
}
class Order
{
public int Quantity;
public Product Product;
}
class Product
{
public string ProdName;
public decimal Price;
}
And I want to use LINQ in C# to print out the names that bought a specific product which in this case is 'ProdName'. I can't find a solution in order to go through all these 3 classes that could give me the name based on the product name.
I have tried something like this but it seems it doesn;t work:
var query = from c in customers where c.Order[0].Product.ProdName.Contains("Milk")
select c.Name;
foreach(var item in query)
{
Console.WriteLine(item);
}
This is how I set up the values for each class:
static public List<Customer> GetCustomerList()
{
var customerList = new List<Customer>
{
new Customer {Name = "Johny", City = "London", Orders = new Order[3] },
new Customer {Name = "Morgan", City = "Copenhagen", Orders = new Order[4]},
new Customer {Name = "Rasmus", City = "Amsterdam", Orders = new Order[1] }
};
return customerList;
}
static public List<Order> GetOrderList()
{
var orderList = new List<Order>
{
new Order { Quantity = 10, Product = new Product()},
new Order { Quantity = 5, Product = new Product()},
new Order { Quantity = 2, Product = new Product()}
};
return orderList;
}
static public List<Product> GetProductList()
{
var productList = new List<Product>
{
new Product { Name = "Cookie, bread", Price = 50 },
new Product { Name = "Cookie, Bread, Milk", Price = 85},
new Product { Name = "bags", Price = 38}
};
return productList;
}
static void Main(string[] args)
{
List<Customer> customers = GetCustomerList();
List<Order> orders = GetOrderList();
List<Product> products = GetProductList();
}
How can I linq all 3 classes together in order to get right result? any hints, please?
You need to build real, related, test data. Some usable fake data setup might look like:
// Create a single instance of each Product that could be used
var egg = new Product { Name = "Eggs", Price = 2.0 };
var bread = new Product { Name = "Bread", Price = 3.0 };
var fooBars = new Product { Name = "FooBars", Price = 2.5 };
var customerList = new List<Customer>
{
new Customer { Name = "Johny", City = "London", Orders = new List<Order>
{
new Order { Quantity = 3, Product = bread },
new Order { Quantity = 1, Product = egg },
new Order { Quantity = 2, Product = fooBars }
}},
new Customer { Name = "Morgan", City = "Copenhagen", Orders = new List<Order>
{
new Order { Quantity = 30, Product = bread }
}},
new Customer { Name = "Rasmus", City = "Amsterdam", Orders = new List<Order>
{
new Order { Quantity = 12, Product = fooBars }
}}
};
Please note that I used List<Order> instead of Order[], but you could switch it back. I also opted for a Name property in Product as you showed in your example code, but which doesn't match your class definition.
Now you can query. Let's see who bought bread:
var whoBoughtBread = customerList
.Where(c => c.Orders.Any(o => o.Product == bread))
.Select(c => c.Name);
Or
var whoBoughtBread2 = customerList
.Where(c => c.Orders.Any(o => o.Product.Name == "Bread"))
.Select(c => c.Name);
With the data structure you have one query would be:
customerList.Where(c => c.Orders.Any(o => o.Product.ProdName == prodName));
but as mentioned in the comments you have potentials for both a null collection and null values within the collection. I would highly recommend making sure you have non-null collections with non-null elements to avoid having to inject null-checking into your query. Otherwise you'll have to do sometihng like:
customerList.Where(c => c.Orders != null &&
c.Orders.Any(o => o != null &&
o.Product != null &&
o.Product.ProdName == prodName));
With C# 6's null-propagation operator (?.) you can shorten it a little:
customerList.Where(c => c.Orders != null &&
c.Orders.Any(o => o?.Product != null &&
o.Product.ProdName == prodName));

c# append new items to list item

The code below has a list with two rows in it, I want to be able to amalgamate the two lines and sum the income field based on the Country.
public class City
{
public string Name {get;set;}
public string Country {get;set;}
public double income {get;set;}
}
public class myClass
{
void Main()
{
var c = GetCities();
var d = c.GroupBy(s => new {s.Country, s.Flag, s.Name}).Select(s => new City { Country = s.Key.Country, Flag = s.Key.Flag, Name = s.Key.Name});
d.Dump(); //LINQPAD output to screen
}
public List<City> GetCities()
{
List<City> cities = new List<City>();
cities.Add(new City() { Name = "Istanbul", income= 22.00, Country = "Turkey" });
cities.Add(new City() { Name = "", income= 44.88, Country = "Turkey" });
return cities;
}
}
in my real application the list is being generated in two places, but the data needs to show on one single line.
found my answer
var result = c.GroupBy(x => x.Country)
.Select(g => {
var item = g.First();
return new{
Country = item.Country,
Name = item.Name,
income = g.Sum(x => x.income) };
}).ToList();

Categories