Merge multiple ObjectList into one - c#

I have a object list like this:
using System;
using System.Collections.Generic;
using System.Linq;
public class Item
{
public int Id;
public int Price;
}
public class Program
{
public static void Main()
{
List<Item> food = new List<Item> {
new Item { Id = 1, Price = 1},
new Item { Id = 2, Price = 3},
new Item { Id = 4, Price = 9}
};
List<Item> drinks = new List<Item> {
new Item { Id = 1, Price = 1},
new Item { Id = 2, Price = 2},
new Item { Id = 3, Price = 0},
new Item { Id = 4, Price = 1},
new Item { Id = 6, Price = 1}
};
List<Item> magazines = new List<Item> {
new Item { Id = 3, Price = 1},
new Item { Id = 5, Price = 2},
};
var combined = food.Union(drinks).Union(magazines).Distinct().ToList();
}
}
What I want to do is, add all the prices into one list. Without any duplicates (Id). My goal is to have the total sum of the prices. So basically add all prices for the same ID together.
So the combined list should look like this:
List<Item> combined = new List<Item> {
new Item { Id = 1, Price = 2},
new Item { Id = 2, Price = 5},
new Item { Id = 3, Price = 1},
new Item { Id = 4, Price = 10},
new Item { Id = 5, Price = 2},
new Item { Id = 6, Price = 1}
};
Preferably using LINQ.

If you need to get a sum of prices for concatenated List<Item>, you should use GroupBy method to group the items by Id and then Sum of prices for every group
var combined = food.Concat(drinks).Concat(magazines)
.GroupBy(i => i.Id, i => i.Price, (i, prices) => new Item { Id = i, Price = prices.Sum() })
.OrderBy(i => i.Id).ToList();
You can also add OrderBy to sort the results by Id property, if it's important

var x =
// First, combine all lists
food.Concat(drinks).Concat(magazines)
// Group combined Items by Id
.GroupBy(item => item.Id)
// From all groups create final Items with Id and summed Price
.Select(g => new Item { Id = g.Key, Price = g.Sum(item => item.Price) });

Related

Linq get id of the employee with most entries in table

i am trying to write a linq that will return the id of the employee who has the most entries in the table.
This is how my class looks like
public class TrainingEmployee
{
public int EmployeeId { get; set; }
public int TrainingId { get; set; }
public List<TrainingEmployee> GenerateData()
{
return new List<TrainingEmployee>()
{
new TrainingEmployee() { EmployeeId = 1, TrainingId = 2},
new TrainingEmployee() { EmployeeId = 1, TrainingId = 2},
new TrainingEmployee() { EmployeeId = 1, TrainingId = 2},
new TrainingEmployee() { EmployeeId = 1, TrainingId = 2},
new TrainingEmployee() { EmployeeId = 2, TrainingId = 3},
new TrainingEmployee() { EmployeeId = 2, TrainingId = 3},
new TrainingEmployee() { EmployeeId = 2, TrainingId = 3},
new TrainingEmployee() { EmployeeId = 2, TrainingId = 3},
new TrainingEmployee() { EmployeeId = 2, TrainingId = 5},
new TrainingEmployee() { EmployeeId = 2, TrainingId = 5},
new TrainingEmployee() { EmployeeId = 2, TrainingId = 1},
};
}
}
And this is how my code looks so far
var lista = new TrainingEmployee();
var data = lista.GenerateData().GroupBy(x => x.EmployeeId);
var maxValue = 0;
var employeeId = 0;
foreach (var group in data)
{
var currentlyGroupCount = group.Count();
if(currentlyGroupCount > maxValue)
{
maxValue = currentlyGroupCount;
employeeId = group.Key;
}
}
Console.WriteLine("Value: {0} employeeid: {1}", maxValue, employeeId);
How can i do the above code in just a linq without using that much of a code?
You could order it descending and select the first one:
var employee = GenerateData()
// group on EmployeeId
.GroupBy(e => e.EmployeeId)
// reverse order it on count
.OrderByDescending(g => g.Count())
// select the first
.FirstOrDefault();
// check if the query returned anything other than default.
if(employee != default)
Console.WriteLine("Value: {0} employeeid: {1}", employee.Count(), employee.EmployeeId);
Another approach similar to jeroen-van-langen's answer but using MoreLINQ's MaxBy():
GenerateData()
.GroupBy(e => e.EmployeeId)
.MaxBy(e => e.Count());
This would also return multiple IDs if multiple employee's had the same "max count"; a possibility in your scenario.
This evaluates Count() once for each employees group so is a little more performant, it allows also to get both of employyId and the max count
var mostFrequentEmployeeId = GenerateData()
.GroupBy(x => x.EmployeeId, (employeeId, employeesGroup) => new { employeeId, count = employeesGroup.Count() })
.OrderByDescending(x => x.count)
.FirstOrDefault()?
.employeeId;

changing item values after grouping with linq

its hard to explain, but I try.
I did a linq grouping.
var grouping = aList.GroupBy(x => new { x.Width, x.Height, x.Quantity});
Lets say I have 3 groups now in grouping. Group 1 has 1 item, group 2 has 5 items and group 3 has 9 items.
All items are having a "stack" field. What I want to do now is, set a number into the stack field of each item based on the group.
All items in group 1 should have the same "stack" number lets say "1", all items in group 2 should have the same "stack" number, maybe "2" and all items in group 3 should have the same "stack" number. So, one "stack" number per group.
How to do that?
Linq is a query and is not meant to modify an existing object. So if you want to modify existing without creating a new then use code below
class Program
{
static void Main(string[] args)
{
List<MyList> aList = new List<MyList>() {
new MyList() { Width = 1, Height = 2, Quantity = 3},
new MyList() { Width = 1, Height = 2, Quantity = 3},
new MyList() { Width = 1, Height = 2, Quantity = 3},
new MyList() { Width = 2, Height = 2, Quantity = 3},
new MyList() { Width = 2, Height = 2, Quantity = 3},
new MyList() { Width = 3, Height = 2, Quantity = 3},
new MyList() { Width = 3, Height = 2, Quantity = 3},
new MyList() { Width = 3, Height = 2, Quantity = 3},
new MyList() { Width = 3, Height = 2, Quantity = 3}
};
var grouping = aList.GroupBy(x => new { x.Width, x.Height, x.Quantity });
int number = 0;
foreach (var group in grouping)
{
foreach (MyList myList in group)
{
myList.StackNumber = number;
}
number++;
}
}
}
public class MyList
{
public int Width { get; set; }
public int Height { get; set; }
public int Quantity { get; set; }
public int StackNumber { get; set; }
}
Select has another overload that passes the index number to the delegate, you could use that.
var grouping = aList
.GroupBy(x => new { x.Width, x.Height, x.Quantity});
.Select( (x,y) => new { Item = x, Stack = y } );

List union that depends / selects on property values

I have a class "item":
public class Item
{
public int Id { get; set; }
public decimal Price { get; set; }
public override bool Equals(object obj)
{
if (obj is Item item)
return item.Id == Id;
return false;
}
// GetHashCode omitted...
}
And I have 2 lists that I need to union:
var items1 = new List<Item>
{
new Item { Id = 1, Price = 10 },
new Item { Id = 2, Price = 10 },
new Item { Id = 3, Price = 10 },
};
var items2 = new List<Item>
{
new Item { Id = 1, Price = 10 },
new Item { Id = 2, Price = 8 },
new Item { Id = 4, Price = 10 },
};
The union I get like this:
var union = items1.Union(items2).ToList();
But I need also the constraint that the items with the lowest price is in the union. So for example in the above lists Item.ID = 2 from "items2" must be in the union...so the result should be a list consisting of these 4 items:
Item { Id = 1, Price = 10 }
Item { Id = 2, Price = 8 } // Not the one with Price = 10
Item { Id = 3, Price = 10 }
Item { Id = 4, Price = 10 }
Is there an elegant way of doing this in C# (preferably using Linq)?
You can try using groupby, like below :
var result = items1.Union(items2).GroupBy(x => x.Id)
.Select(x => new Item
{
Id = x.Key,
Price = x.Min(i => i.Price)
});

LINQ Remove duplicates and result item to have sum of quantities

I just want to check if there is quicker way using LINQ to have list removed from duplicates by id, but in result list item will have sum of some other property (in this case Price). For example:
Start list:
List<Item> a = new List<Item>
{
new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
new Item {Id = 2, Name = "Item2", Code = "IT00002", Price = 200},
new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
new Item {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}
};
And result list would be:
List<Item> a = new List<Item>
{
new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 200},
new Item {Id = 2, Name = "Item2", Code = "IT00002", Price = 200},
new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 550}
};
In (functional) LINQ it is something like:
List<Item> b = a
.GroupBy(x => x.Id)
.Select(x => new Item { Id = x.Key, Name = x.First().Name, Code = x.First().Code, Price = x.Sum(y => y.Price) })
.ToList();
In keyword-based LINQ it is something like:
List<Item> c = (from x in a
group x by x.Id into y
select new Item { Id = y.Key, Name = y.First().Name, Code = y.First().Code, Price = y.Sum(z => z.Price) }
).ToList();
var filteredList = a.GroupBy(e => e.Id).Select(g =>
{
var item = g.First();
return new Item
{
Id = item.Id,
Name = item.Name,
Code = item.Code,
Price = g.Sum(e => e.Price)
};
}).ToList();
Something like this will do..
var result = a.GroupBy(it => new { it.Id, it.Name, it.Code })
.Select(x => new { x.Key.Id,x.Key.Name,x.Key.Code,Price = x.Sum(y=>y.Price)});
public class Item : IEquatable<Item>
{
public int? Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
public int? Price { get; set; }
public bool Equals(Item Other)
{
//Check whether the compared object is null.
if (Object.ReferenceEquals(Other, null)) return false;
//Check whether the compared object references the same data.
if (Object.ReferenceEquals(this, Other)) return true;
//Check whether the products' properties are equal.
return this.Id == Other.Id;
}
public override int GetHashCode()
{
return this.Id.GetHashCode();
}
}
List<Item> a = new List<Item>
{
new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
new Item {Id = 2, Name = "Item2", Code = "IT00002", Price = 200},
new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
new Item {Id = 1, Name = "Item1", Code = "IT00001", Price = 100},
new Item {Id = 3, Name = "Item3", Code = "IT00003", Price = 150},
new Item {Id = 3, Name = "Item3", Code = "IT00004", Price = 250}
};
var b = a.Distinct().ToList();

C# LINQ Take limited results per grouped

I have list that includes class named 'ID', 'Name' and 'Category'. There are 6 item in list.
List<MyData> list =
{
{0, "John", "Police"},
{1,"Michael", "Police"},
{2,"Alice", "Police"},
{3, "Ferdinand", "Thief"},
{4, "Jocas", "Thief"},
{5, "Connor", "Thief"}
};
I wanna list them with limited quantity per group by 'Category' with LINQ.
Example : I want list 2 item for each 'Cateogory'. Listed should be below :
John Police
Michael Police
Ferdinand Thief
Jocas Thief
Use combination of Take and SelectMany:
var results = list.GroupBy(x => x.Category).SelectMany(g => g.Take(2)).ToList();
I've tested it on following Item class:
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public string Category { get; set; }
}
And query:
List<Item> list = new List<Item>
{
new Item { ID = 0, Name = "John", Category = "Police"},
new Item { ID = 1, Name = "Michael", Category = "Police"},
new Item { ID = 2, Name = "Alice", Category = "Police"},
new Item { ID = 3, Name = "Ferdinand", Category = "Thief"},
new Item { ID = 4, Name = "Jocas", Category = "Thief"},
new Item { ID = 5, Name = "Connor", Category = "Thief"}
};
var results = list.GroupBy(x => x.Category).SelectMany(g => g.Take(2)).ToList();
Returns 4 elements, right as you want.

Categories