Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
As example I have the following list of PlanesLogRow objects
public class PlanesLogRow
{
public DateTime ArriveDate;
public string Origin;
public string Destination;
}
Need to get list of all of airports (Origin + Destination).Distinct()
Need to calc for every airport "arrived to airport" and "left from the airport" counts.
I need to create by one LINQ string tuple like <airport, AsOriginCount(arrivedToCount), AsDestinationCount(LeftFromCount)>
To get list of all of airports isn't a problem, but not sure how this can be done in case of such double grouping by different parameters
If you have list of planes you can project each plane object into two anonymous objects - one for destination, and one for origin. Then group these anonymous objects by airport and calculate totals:
planes.SelectMany(p => new[] {
new { Airport = p.origin, IsOrigin = true },
new { Airport = p.destination, IsOrigin = false }
})
.GroupBy(x => x.Airport)
.Select(g => new {
Airport = g.Key,
AsOriginCount = g.Count(x => x.IsOrigin),
AsDestinationCount = g.Count(x => !x.IsOrigin)
})
For given planes:
var planes = new List<Plane> {
new Plane { Origin = "Minsk", Destination = "London" },
new Plane { Origin = "Barcelona", Destination = "Minsk" },
new Plane { Origin = "Rome", Destination = "Minsk" },
new Plane { Origin = "Barcelona", Destination = "London" },
new Plane { Origin = "London", Destination = "Rome" },
};
Output will be:
[
{ "Airport": "Minsk", "AsOriginCount": 1, "AsDestinationCount": 2 },
{ "Airport": "London", "AsOriginCount": 1, "AsDestinationCount": 2 },
{ "Airport": "Barcelona", "AsOriginCount": 2, "AsDestinationCount": 0 },
{ "Airport": "Rome", "AsOriginCount": 1, "AsDestinationCount": 1 }
]
Update: This query will work with Entity Framework. Generated SQL will be big and scary.
Related
Suppose I have the following object
public string Quote { get; set; }
public string FirstName { get; set; }
I have a List of this object, sample data as:
I am trying to return a new list which will evenly distribute between each FirstName (Same amount) and return 50% of the rows.
In the above Example, I have 20 rows, returning half gives 10. There are 4 different FirstNames to which John = 2, Mark = 2, Phil = 2, bob = 2 - There are 2 slots remaining which 2 Different random names are chosen.
So how do i do the grouping so the FirstNames are evenly distributed? and is the 50% taken first or last?
New to LINQ and filtering through data so help is appreciated :)
LinQ is not the most efficient way As we will be ieterating multiple time the same collection in roder to get basic count etc.
But lets do it Step by step.
Data generation:
That part was missing from the original question. You can change the repatition of name adding duplicate values in names collection.
// Data generation, Tuple because Im lazy.
var names = new[] { "Toto", "John", "John", "John", "Foo", "Foo", "Bar" };
var datas = Enumerable.Range(0, 100)
.Select(x => (Id: x + 1, Name: names[x % names.Length]));
Basic Count:
As we are given a percentage we need to know how many element it represent.
// Input value
var wantedPercent = 10;
// Counting, may be more efficient to iterate that list only once doing grouping and both count in one go
var totalCount = datas.Count();
var distinctNames = datas.Select(x => x.Name).Distinct().Count();
int wantedSize = (int)Math.Ceiling(totalCount * (wantedPercent / 100.0));
int maxGroupSize = wantedSize / distinctNames; // Integer division : 1.9999 -> 1
Grouping:
This is the core issue. We need to group by on name.
And for each element of those group we will number them based on maxsize of a group.
If that number exceed the max size we keep them for remaining slots.
var grpDatas = datas.GroupBy(x => x.Name)
.SelectMany(g =>
g.Select((x, i) =>
new { Item =x, newGroupingKey = i < maxGroupSize}
)
)
.GroupBy(x=> x.newGroupingKey);
Populate :
Let's take the item of the first group.
var result = grpDatas.First(x=> x.Key)
.Select(g=> g.Item)
.ToList();
You can have a remaining slot even if wantedSize / distinctNames is round.
Because one group size may be lower than the Max Allowed Size.
var remainingSlot = wantedSize - result.Count();
if (remainingSlot > 0){
var random = new Random();
var shuffledGroup2 = grpDatas.First(x => !x.Key)
.Select(g => g.Item)
.OrderBy(a => random.Next());
var remaining = shuffledGroup2.Take(remainingSlot);
result.AddRange(remaining);
}
Result : Live Demo
[
{ "Id": 1, "Name": "Toto" },
{ "Id": 8, "Name": "Toto" },
{ "Id": 2, "Name": "John" },
{ "Id": 3, "Name": "John" },
{ "Id": 5, "Name": "Foo" },
{ "Id": 6, "Name": "Foo" },
{ "Id": 7, "Name": "Bar" },
{ "Id": 14, "Name": "Bar" },
{ "Id": 83, "Name": "Foo" }, // random
{ "Id": 85, "Name": "Toto" } // random
]
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
Suppose I have the following collection:
ONE
- Banana
- Mango
TWO
- Apple
- Mango
THREE
- Orange
- Pear
I want to get only the collection which do not have Mango in it, such as:
THREE
- Orange
- Pear
The following example returns wrong result:
List<Order> list = new List<Order> {
new Order { Id = 1, Name = "ONE", Items = new List<Items> { new Item { Id = 1, Nama = "Banana" }, new Items { Id = 2, Nama = "Mango" } }},
new Order { Id = 1, Name = "TWO", Items = new List<Items> { new Item { Id = 1, Nama = "Orange" }, new Items { Id = 2, Nama = "Mango" } }},
new Order { Id = 1, Name = "THREE", Items = new List<Items> { new Item { Id = 1, Nama = "Pear" }, new Items { Id = 2, Nama = "Chery" } }},
};
var result = list.Where(x => x.Item.Any(y => y.Nama != "Mango")).ToList();
Any will bail out as soon as a "Nama" different from 'Mango' is found which is not what you want.
x.Items.All(y => y.Nama != "Mango")
Should do it.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I am trying to merge this two collection into one list but one of the collection is List<IGrouping<T>>. I am trying to merge the groupedSubscriptions (which is a List<IGrouping<int, Subscription>>) with the ListOfSubscriptionsSecond (which is a List<Subscription>) to create the FullListOfSubscriptions which is, finally, a List<Subscription>. See code sample below.
I'm getting a compile time error:
Severity Code Description Project File Line Suppression State Suppression State
Error CS1503 Argument 1: cannot convert from 'System.Collections.Generic.IEnumerable<System.Linq.IGrouping<int, ListObject.Program.Subscription>>' to 'System.Collections.Generic.IEnumerable<ListObject.Program.Subscription>'
Logical example of what I'm trying
List<Subscription> FullListOfSubscriptions = new List<Subscription>();
List<Subscription> ListOfSubscriptions = new List<Subscription>();
ListOfSubscriptions.Add(new Subscription() { SubscriptionId = 1, ParentProductId = 4, ChildProductId = 4, ParentProductName = "Product 1", ChildProductName = "Product 1", GroupId = 362 });
ListOfSubscriptions.Add(new Subscription() { SubscriptionId = 2, ParentProductId = 114, ChildProductId = 1, ParentProductName = "Product 2", ChildProductName = "Product 3", GroupId = 1 });
ListOfSubscriptions.Add(new Subscription() { SubscriptionId = 3, ParentProductId = 114, ChildProductId = 2, ParentProductName = "Product 2", ChildProductName = "Product 4", GroupId = 1 });
var groupedSubscriptions = ListOfSubscriptions.GroupBy(u => u.GroupId);
var flattenedSubscriptions = groupedSubscriptions.SelectMany(grp => grp.AsEnumerable()).ToList();
List<Subscription> ListOfSubscriptionsSecond = new List<Subscription>();
ListOfSubscriptionsSecond.Add(new Subscription() { SubscriptionId = 4, ParentProductId = 4, ChildProductId = 4, ParentProductName = "Product 1", ChildProductName = "Product 1", GroupId = 362 });
ListOfSubscriptionsSecond.Add(new Subscription() { SubscriptionId = 5, ParentProductId = 114, ChildProductId = 1, ParentProductName = "Product 2", ChildProductName = "Product 3", GroupId = 1 });
ListOfSubscriptionsSecond.Add(new Subscription() { SubscriptionId = 6, ParentProductId = 114, ChildProductId = 2, ParentProductName = "Product 2", ChildProductName = "Product 4", GroupId = 1 });
//FullListOfSubscriptions = ListOfSubscriptionsSecond.AddRange(groupedSubscriptions);
FullListOfSubscriptions = groupedSubscriptions
.SelectMany(grp => grp.AsEnumerable())
.Concat(ListOfSubscriptionsSecond); // <<= I'm getting error here
Really depends on what exactly you want to do. Let's say you have a class like below
public class Employee
{
public string Department { get; set; }
public string Name { get; set; }
}
Then you can merge like
public IEnumerable<Employee> Merge(IEnumerable<Employee> employees,
IEnumerable<IGrouping<string, Employee>> employeesByDept)
{
// Just merge all employees
return employeesByDept
.SelectMany(grp => grp.AsEnumerable()) // flatten
.Concat(employees); // concat the other
}
If you want to merge the whole set as IGrouping, there can be many ways of doing that, one of that is just flatten the already grouped one, concat the other list and group again.
public IEnumerable<IGrouping<string, Employee>> MergeGroup(IEnumerable<Employee> employees,
IEnumerable<IGrouping<string, Employee>> employeesByDept)
{
return employeesByDept
.SelectMany(grp => grp.AsEnumerable()) // flatten
.Concat(employees) // concat the other
.GroupBy(e => e.Department); // group again
}
Few thigs to understand here:
IEnumerable is a high level collections interface, which most of the collections (if not all) implement. These are only meant for iteration i.e. going over the collection & reading data. It does not allow modification of any sort.
List is a commonly used implementation of IEnumerable, which is a concrete class AND also supports modification.
A List<T> can be assigned to an IEnumerable<T>, but not vice-versa. Learn about Covariance and Contravariance.
And learn about IGrouping.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
There is the next data structure:
var parents = new List<Parent>
{
new Parent
{
Children = new List<Child>
{
new Child { Name = "Name1", Value = 100 },
new Child { Name = "Name2", Value = 0 },
}
},
new Parent
{
Children = new List<Child>
{
new Child { Name = "Name1", Value = 0 },
new Child { Name = "Name2", Value = 200 },
}
},
new Parent
{
Children = new List<Child>
{
new Child { Name = "Name1", Value = 0 },
new Child { Name = "Name2", Value = 200 },
}
},
new Parent
{
Children = new List<Child>
{
new Child { Name = "Name1", Value = 100 },
new Child { Name = "Name2", Value = 0 },
}
}
};
Parents have the similar children but its values can be binary different (0 or some value).
The output should be:
var output = new List<Child>
{
new Child { Name = "Name1", Value = 100 },
new Child { Name = "Name2", Value = 200 },
};
Is there efficient and compact way to retrieve data in this way via LINQ or extension methods?
Most of the answers are very close but they have small imperfections, in my humble opinion. Here's how I would write it:
var output = parents
.SelectMany(p => p.Children)
.Where(c => c.Value != 0)
.GroupBy(c => c.Name)
.Select(g => g.First())
.ToList();
Maybe I'm misunderstanding but can't you just use SelectMany ?
parents.SelectMany(x=>x.Children).Where(x=>x.Value != 0).Distinct().ToList();
edit: Added .Distinct() per #GSerg's comment
This avoids custom Equality operator or copies of your objects:
parents.SelectMany(p => p.Children).GroupBy(c => c.Name).Select(g => g.FirstOrDefault(c
=> c.Value != 0)).ToList().
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
how to convert to lambda:
var source = new Item[]{
new Item { Key = "A", ID = 1, Text = "Hello" },
new Item { Key = "A", ID = 2, Text = "World" },
new Item { Key = "B", ID = 2, Text = "Bar" },
new Item { Key = "B", ID = 1, Text = "Foo" }
};
var results = (from r in source
group r by r.Key.ToString() into g
select new
{
g.Key,
Data = string.Join("", g.OrderBy(s => s.ID).Select(s => s.Text))
});
It is possible to convert?
Thanks for answer
How about this?
var results = source.GroupBy(r => r.Key).Select(g => new
{
g.Key,
Data = string.Join("", g.OrderBy(s => s.ID).Select(s => s.Text))
});