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();
Related
I haw this model:
public class Obj1
{
public long ID { get; set; }
public long Name { get; set; }
public List<int> NumberList { get; set; }
}
The values in Obj1 List:
List<Obj1> lst1 = new List<Obj1>();
lst1.Add(new Obj1()
{
ID = 1,
Name = "t1",
NumberList = new List<int>{1,3,4}
});
lst1.Add(new Obj1()
{
ID = 2,
Name = "t2",
NumberList = new List<int>{1,4,5}
});
lst1.Add(new Obj1()
{
ID = 3,
Name = "t3",
NumberList = new List<int>{4,5,6}
});
lst1.Add(new Obj1()
{
ID = 4,
Name = "t4",
NumberList = new List<int>{5,7,8}
});
I want to select list1 provided all list2 is in it. Also list 2 is equal to:
List<int> lst2 = new List<int>(){4,5};
I use this code, But it doesn't work properly:
var FinalList = lst1.Where(item => item.NumberList.Any(item2 => lst2.Contains(item2)).ToList();
The correct output should be this:
{
ID = 2,
Name = "t2",
NumberList = new List<int>{1,4,5}
},
{
ID = 3,
Name = "t3",
NumberList = new List<int>{4,5,6}
}
If I understand correctly, you want all Obj items in lst1 if the NumberList property is a superset of lst2. You can query this like so:
var finalList = lst1.Where(l => !lst2.Except(l.NumberList).Any()).ToList();
Full example on Ideone.
Incoming list
var list = new List<Franchise>()
{
new Franchise()
{Id = 10, Name = "Franchise1", Code= "DD1", IsDomestic= 1, ParentCompanyId=1, GroupId=100 },
new Franchise()
{Id = 10, Name = "Franchise1", Code= "DD1", IsDomestic= 1, ParentCompanyId=2, GroupId=100 },
new Franchise()
{Id = 10, Name = "Franchise1", Code= "DD1", IsDomestic= 1, ParentCompanyId=3, GroupId=200 },
new Franchise()
{Id = 15, Name = "Franchise5", Code= "FD1", IsDomestic= 0, ParentCompanyId=4, GroupId=300 },
new Franchise()
{Id = 15, Name = "Franchise5", Code= "FD1", IsDomestic= 0, ParentCompanyId=3, GroupId=300 },
new Franchise()
{Id = 15, Name = "Franchise5", Code= "FD1", IsDomestic= 0, ParentCompanyId=2, GroupId=400 },
};
I want this to be transformed to list of the class below using LINQ
public class FranchiseNew
{
public int Id { get; set; }
public string Name{ get; set; }
public int[] CategoryIds { get; set; }
public int[] DivisionIds { get; set; }
public int IsDomestic{ get; set; }
}
output - one row per franchise with ParentCompanyIds and GroupIds in arrays
var list = new List<Franchise>()
{
new Franchise()
{Id = 10, Name = "Franchise1", Code= "DD1", IsDomestic= 1, ParentCompanyIds=[1, 2, 3], GroupIds = [100, 200 ]},
new Franchise()
{Id = 15, Name = "Franchise2", Code= "FD1", IsDomestic= 0, ParentCompanyIds=[4, 3, 2], GroupIds = [300, 400] }
};
What is the efficient LINQ query to achieve this? Thanks in advance.
You can try like below:
var collectionGroup = list.GroupBy(x => new { x.Name, x.Id, x.Code, x.IsDomestic }).ToList();
var result = collectionGroup.Select(x => new FranchiseNew
{
Id = x.Key.Id,
Name = x.Key.Name,
Code = x.Key.Code,
IsDomestic = x.Key.IsDomestic,
CategoryIds = x.GroupBy(s => s.ParentCompanyId).Select(y => y.Key).ToArray(),
DivisionIds = x.GroupBy(s => s.GroupId).Select(y => y.Key).ToArray()
}).ToList();
And in you're FranchiseNew model, add Code field.
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) });
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)
});
I have a question about a LINQ grouping.
I thought that grouping would be a simple matter of using the GroupBy function on the result set and specifying what to group it by. However my items appear to not be grouping together and instead are displaying as if the GroupBy function wasn't there. I want to group by the itemPk, but I'm can't seem to do it. I have tried grouping by both category.ItemFk and Item.Itempk, but no luck. Could someone give me a pointer on this?
var itemIds = items.Select(i => i.ItemId).ToList();
var itemAndCatJoin =
from item in Context.SCS_Items
join category in Context.SCS_ItemCategories
on item.ItemPk equals category.ItemFk
into temp
from category in temp.DefaultIfEmpty()
select new ExportItemTable
{
Category = category,
Item = item
};
return itemAndCatJoin.Where(i => itemIds.Contains(i.Item.ItemPk))
.GroupBy(n => new {n.Item, n.Category})
.Select(i => new ExportableItem
{
ItemPk = i.Key.Item.ItemPk,
Name = i.Key.Item.Name,
Description = i.Key.Item.Description,
Price = i.Key.Item.Price,
Category = i.Key.Category.Category.Category_Name,
GLDepartment = i.Key.Category.GL_Department.Name ?? "",
GLName = i.Key.Category.GL_Name.Name ?? "",
StartDate = i.Key.Item.StartDate,
EndDate = i.Key.Item.EndDate,
FiscalYear = i.Key.Item.SCS_FiscalYear.Name,
School = i.Key.Item.School != null ? i.Key.Item.School.School_Name : i.Key.Item.Board.Board_Name,
Beneficiary = i.Key.Item.SCS_Beneficiary.Name,
Quantity = i.Key.Item.MaxQuantity,
Deleted = i.Key.Item.DeletedFlag,
OptionalStudents = i.Key.Item.SCS_Attachments.Where(a => !a.IsRequired).SelectMany(a => a.SCS_StudentAttachments).Where(s => !s.DeletedFlag).Select(s => s.StudentFk).Distinct().Count(),
RequiredStudents = i.Key.Item.SCS_Attachments.Where(a => a.IsRequired).SelectMany(a => a.SCS_StudentAttachments).Where(s => !s.DeletedFlag).Select(s => s.StudentFk).Distinct().Count(),
IsPublic = i.Key.Item.IsPublic,
AllowRecurring = i.Key.Item.AllowRecurringPayments,
EffectiveCutoff = i.Key.Item.SCS_Attachments.Where(a => !a.DeletedFlag && a.CourseDropCutoff.HasValue).Select(a => a.CourseDropCutoff).OrderBy(a => a).FirstOrDefault(),
CreatedDate = i.Key.Item.CreatedDate
}).OrderBy(i => i.ItemPk).ToList();
}
your groupbyy is indeed doing nothing for you, you need to tell the groupby what to group by....
like
.GroupBy(n => n.Category)
Here is a simple example to your grouping question:
class Program
{
static void Main()
{
var allItems = GetAllItems();
var groups = from item in allItems
group item by item.Category
into newGroup
select newGroup;
foreach (var group in groups)
{
Console.WriteLine($"\nCategory: {group.Key}");
foreach (var item in group)
{
Console.WriteLine($"{item.Name}: {item.Price}");
}
}
Console.ReadLine();
}
static List<Category> GetAllCategories()
{
return new List<Category>()
{
new Category() { Id = 1, Name = "Programming Books" },
new Category() { Id = 2, Name = "Fiction Books" }
};
}
static List<Item> GetAllItems()
{
return new List<Item>()
{
new Item() { Id = 1, Name = "Embedded Linux", Category = 1, Price = 9.9 },
new Item() { Id = 2, Name = "LINQ In Action", Category = 1, Price = 36.19 },
new Item() { Id = 3, Name = "C# 6.0 and the .NET 4.6 Framework", Category = 1, Price = 40.99 },
new Item() { Id = 4, Name = "Thinking in LINQ", Category = 1, Price = 36.99 },
new Item() { Id = 5, Name = "The Book Thief", Category = 2, Price = 7.99 },
new Item() { Id = 6, Name = "All the Light We Cannot See", Category = 2, Price = 16.99 },
new Item() { Id = 7, Name = "The Life We Bury", Category = 2, Price = 8.96 }
};
}
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public double Price { get; set; }
public int Category { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
This example is simple enough for anyone new to LINQ. I am sure you can make some adjustment to make it work for your specific issue. Hope this will help.