Dynamically Pivot in LINQ: Pivoted Columns Determined at Runtime - c#

Let's say I have three tables. Basically, one table of things, one table describing the possible attributes of the things, and a bridge table providing the values for those attributes for a particular thing. This lets you do dynamic metadata.
Users can add metadata properties at any time, and then they can provide values for those properties for any thing in the "things" table.
So like this:
Table: Persons
PersonID | FirstName | LastName
1 | Bob | Jones
2 | Fred | Smith
3 | Sally | Doe
Table: Properties
PropertyID | Name
1 | SupervisorName
2 | Age
3 | Birthday
4 | EmployeeNumber
5 | Hometown
Table: PropertyValues
PersonID | PropertyID | PropertyValue
1 | 1 | Frank Grimes
1 | 2 | 47
2 | 2 | 35
2 | 4 | 1983738
2 | 3 | 5/5/1978
3 | 3 | 4/4/1937
3 | 5 | Chicago, IL
So, users want to be able to view a report of these properties. Maybe I want to see a table containing the age and birthday of all employees. There would be blanks in the table if those values aren't populated for those users. Then maybe I want to see a report that includes supervisor, age and birthday--I should be able to generate that table on the fly as well.
Now, in order to do this with SQL, I would dynamically construct a query and add a join to that query for each property that I want to pivot up to the top. That's how it works now.
If I wanted to do this in LINQ, and I knew which properties to pivot while writing the code, I could do that too--I'd just use GroupJoin().
What I don't know is how to dynamically construct a LINQ query that would allow me to pivot any number of properties at runtime, without knowing what they are ahead of time.
Any ideas?
(Before you knee-jerk mark this as a duplicate, know that I did a fair amount of StackOverflow research before posting this question, and if this exact question has been asked before, I couldn't find it.)

You can build linq expression trees dynamically. This topic is covered (including example) in the following MSDN article: http://msdn.microsoft.com/en-us/library/bb882637.aspx
My suggestion would be to write an example Linq query for your task and rebuild it programmatically with expression trees. Once it works you adapt it and inject your dynamic parts.

My thought is you could join with a where condition as follows:
Person Class
public class Person
{
public int Id {get; set;}
public string FirstName {get; set;}
public string LastName {get; set;}
}
Property Class
public class Property
{
public int Id {get; set;}
public string Name {get; set;}
}
Value Class
public class Value
{
public int PersonId {get; set;}
public int PropertyId {get; set;}
public string Val {get; set;}
}
Code
void Main()
{
var selectBy = "Birthday";
var persons = new List<Person>() { new Person {Id = 1, FirstName = "Bob", LastName = "Jones"}, new Person {Id = 2, FirstName = "Fred", LastName = "Smith"}, new Person {Id = 3,FirstName = "Sally", LastName = "Doe"}};
var properties = new List<Property>() { new Property {Id = 1, Name = "SupervisorName"}, new Property {Id = 2, Name = "Age"}, new Property {Id = 3, Name = "Birthday"}, new Property {Id = 4, Name = "EmployeeNumber"}, new Property {Id = 5, Name = "Hometown"}};
var values = new List<Value>() { new Value {PersonId = 1, PropertyId = 1, Val="Frank Grimes"}, new Value {PersonId = 1, PropertyId = 2, Val="47"}, new Value {PersonId = 2, PropertyId = 2, Val="35"}, new Value {PersonId = 2, PropertyId = 4, Val="1983738"}, new Value {PersonId = 2, PropertyId = 3, Val="5/5/1978"}, new Value {PersonId = 3, PropertyId = 3, Val="4/4/1937"}, new Value {PersonId = 3, PropertyId = 5, Val="Chicago, IL"}};
var result = from v in values
join p in persons on v.PersonId equals p.Id
join p2 in properties on v.PropertyId equals p2.Id
where p2.Name.Equals(selectBy)
select new { Name = p.FirstName + " " + p.LastName,
Value = v.Val
};
result.Dump();
}
Results
Name, Value
Fred Smith 5/5/1978
Sally Doe 4/4/1937
Revised Answer
void Main()
{
var selectBy = "Birthday";
var persons = new List<Person>() { new Person {Id = 1, FirstName = "Bob", LastName = "Jones"}, new Person {Id = 2, FirstName = "Fred", LastName = "Smith"}, new Person {Id = 3,FirstName = "Sally", LastName = "Doe"}};
var properties = new List<Property>() { new Property {Id = 1, Name = "SupervisorName"}, new Property {Id = 2, Name = "Age"}, new Property {Id = 3, Name = "Birthday"}, new Property {Id = 4, Name = "EmployeeNumber"}, new Property {Id = 5, Name = "Hometown"}};
var values = new List<Value>() { new Value {PersonId = 1, PropertyId = 1, Val="Frank Grimes"}, new Value {PersonId = 1, PropertyId = 2, Val="47"}, new Value {PersonId = 2, PropertyId = 2, Val="35"}, new Value {PersonId = 2, PropertyId = 4, Val="1983738"}, new Value {PersonId = 2, PropertyId = 3, Val="5/5/1978"}, new Value {PersonId = 3, PropertyId = 3, Val="4/4/1937"}, new Value {PersonId = 3, PropertyId = 5, Val="Chicago, IL"}};
// Default Values for the Cartesian Product
var defaultValues = new string[]{"","","","",""};
// propertyKeys are used to filter values generated for pivot table
var propertyKeys = new List<Property> { new Property{Id=1}, new Property{Id=2}, new Property{Id=3}};
// Generate default values for every person and each property
var cartesianProduct = from ppl in persons
from prop in properties
join pk in propertyKeys on prop.Id equals pk.Id
select new {PersonId = ppl.Id, PropertyId = prop.Id, Val = defaultValues[prop.Id-1]};
// Create Pivot Values based on selected PropertyIds
var newValues = from cp in cartesianProduct
join v in values on new {cp.PersonId, cp.PropertyId} equals new { v.PersonId, v.PropertyId } into gj
from x in gj.DefaultIfEmpty()
select new {
PersonId = (x == null ? cp.PersonId : x.PersonId),
PropertyId = (x == null ? cp.PropertyId: x.PropertyId),
Val = ( x == null ? cp.Val : x.Val )
};
foreach( var y in newValues )
{
var aPerson = persons.Where( r=> r.Id == y.PersonId ).First().FirstName;
var aProperty = properties.Where( r=> r.Id == y.PropertyId ).First().Name;
Console.WriteLine(string.Format("{0:12} {1:12} {2:12}", aPerson, aProperty, y.Val));
}
}
Results:
Bob | SupervisorName | Frank Grimes
Bob | Age | 47
Bob | Birthday |
Fred | SupervisorName |
Fred | Age | 35
Fred | Birthday | 5/5/1978
Sally | SupervisorName |
Sally | Age |
Sally | Birthday | 4/4/1937

Related

How to get average value using LEFT JOIN in LINQ and Lambda

I want to display each book average rating with its publisher using LINQ and Lambda.
Here's my book list
private List<Book> Books = new List<Book>(){
new Book {
Id: 1,
Name: Book A,
PublisherId: 1
}, new Book {
Id: 2,
Name: Book B,
PublisherId: 2
}, new Book {
Id: 3,
Name: Book C,
PublisherId: 1
}
};
First, i Join Book list with Publisher list using this query
var bookPublisher = Books.SelectMany( book => Publishers.Where(publisher => book.PublisherId == publisher.Id),
(book, publisher) => new { book, publisher });
Then, i try to use left join with BookTransaction list to get detail rating for each book. Here's my BookTransaction list.
private List<Book_Transaction> BookTransactions = new List<Book_Transaction>() {
new Book_Transaction {
Id = 1,
BookId = 1,
CustomerId = 1,
RatingStar = 4.5
}, new Book_Transaction {
Id = 2,
BookId = 2,
CustomerId = 1,
RatingStar = 4
}, new Book_Transaction {
Id = 3,
BookId = 1,
CustomerId = 2,
RatingStar = 5
},new Book_Transaction {
Id = 4,
BookId = 2,
CustomerId = 2,
RatingStar = 3.5
},new Book_Transaction {
Id = 5,
BookId = 1,
CustomerId = 3,
RatingStar = 4
}};
and here's my query
var bookPublisherRating = bookPublisher.SelectMany(bp => BookTransactions.Where(bt => bp.book.Id == bt.BookId).DefaultIfEmpty(),
(bp, bt) => new { BookPublish = bp, BookRating = bt.RatingStar });
in that query, i got the error saying that "'Object reference not set to an instance of an object.' bt was null."
I tried using different approach, but the result it just the same error.
var bookPublisherRating = bookPublisher.GroupJoin(BookTransactions,
bp => bp.book.Id,
bt => bt.BookId,
(bp, bt) => new { BookPublish = bp, BookTrans = bt })
.SelectMany(temp => temp.BookTrans.DefaultIfEmpty(),
(temp, y) => new { BookPublish = temp.BookPublish, bookRating = y.RatingStar });
Can someone tell me where i was wrong? When i deleted the DefaultIfEmpty function, it can display the data but only where both list have the bookId value included, like this
| BookName | PublisherName | Rating |
| Book A | Publisher A | 4.5 |
| Book A | Publisher A | 5 |
| Book A | Publisher A | 4 |
| Book B | Publisher B | 4 |
| Book B | Publisher B | 3.5 |
I expect the result of the query just like this
| BookName | PublisherName | Rating |
| Book A | Publisher A | 4.5 |
| Book B | Publisher B | 3.75 |
| Book C | Publisher A | 0 |
Linq has an Average Method. You can call it on a collection of numeric values, or for a collection of objects you can pass it the property name to average. You can also call GroupBy to create key<->collection pairs.
So you can group your data by the BookId and find the average rating.
Assuming the following
public class Book_Transaction
{
public int Id { get; set; }
public int BookId { get; set; }
public int CustomerId { get; set; }
public double RatingStar { get; set; }
}
var bookTransactions = new List<Book_Transaction>() {
new Book_Transaction {
Id = 1,
BookId = 1,
CustomerId = 1,
RatingStar = 4.5
}, new Book_Transaction {
Id = 2,
BookId = 2,
CustomerId = 1,
RatingStar = 4
}, new Book_Transaction {
Id = 3,
BookId = 1,
CustomerId = 2,
RatingStar = 5
},new Book_Transaction {
Id = 4,
BookId = 2,
CustomerId = 2,
RatingStar = 3.5
},new Book_Transaction {
Id = 5,
BookId = 1,
CustomerId = 3,
RatingStar = 4
}};
Then
bookTransactions.GroupBy(
bt => bt.BookId,
(bookId, collection) => new {
BookId = bookId,
AverageRating = collection.Average(x => x.RatingStar)
}).ToList()
Gives the following output in the C# interactive console:
List<<>f__AnonymousType0#5<int, double>>(2) {
{ BookId = 1, AverageRating = 4.5 },
{ BookId = 2, AverageRating = 3.75 }
}

Creating a Nested List from a Flat List in C#

I currently have the following classes:
public class NavigationItem
{
public int ID { get; set; }
public string Title { get; set; }
public int ParentID { get; set; }
public List<NavigationItem> Children { get; set; }
}
public class FlatItem
{
public int ID { get; set; }
public string Title { get; set; }
public int ParentID { get; set; }
}
I have a sample data as follows:
+====+============+==========+
| ID | Title | ParentID |
+====+============+==========+
| 1 | Google | |
+----+------------+----------+
| 2 | Microsoft | |
+----+------------+----------+
| 3 | Oracle | |
+----+------------+----------+
| 4 | Gmail | 1 |
+----+------------+----------+
| 5 | Sheets | 1 |
+----+------------+----------+
| 6 | Adsense | 1 |
+----+------------+----------+
| 7 | Azure | 2 |
+----+------------+----------+
| 8 | SharePoint | 2 |
+----+------------+----------+
| 9 | Office | 2 |
+----+------------+----------+
| 10 | Java | 3 |
+----+------------+----------+
| 11 | Word | 9 |
+----+------------+----------+
| 12 | Excel | 9 |
+----+------------+----------+
| 13 | PowerPoint | 9 |
+----+------------+----------+
I already have the code to pull all the information from the sample data above and turn it into a List<FlatItem> object.
What's the best approach so that I can have a List<NavigationItem> object which will look like something below:
Google
Gmail
Sheets
AdSense
Microsoft
Azure
SharePoint
Office
Word
Excel
PowerPoint
Oracle
Java
I'm thinking of creating a recursive method to loop through my List<FlatItem> then structure it in a way to be a nested list of NavigationItem.
No need for recursion. You could use LINQ to build the structure easily:
List<FlatItem> flatItems = ...;
var navigationItems = flatItems.Select(
i => new NavigationItem { ID = i.ID, Title = i.Title, ParentID = i.ParentID }
).ToList();
foreach (var i in navigationItems)
i.Children = navigationItems.Where(n => n.ParentID == i.ID).ToList();
// get Google, Microsoft, Oracle items
var rootNavigationItems = navigationItems.Where(n => n.ParentID == 0);
Try this:
List<FlatItem> source = new List<UserQuery.FlatItem>()
{
new FlatItem() { ID = 1, Title = "Google", ParentID = null },
new FlatItem() { ID = 2, Title = "Microsoft", ParentID = null },
new FlatItem() { ID = 3, Title = "Oracle", ParentID = null },
new FlatItem() { ID = 4, Title = "Gmail", ParentID = 1 },
new FlatItem() { ID = 5, Title = "Sheets", ParentID = 1 },
new FlatItem() { ID = 6, Title = "Adsense", ParentID = 1 },
new FlatItem() { ID = 7, Title = "Azure", ParentID = 2 },
new FlatItem() { ID = 8, Title = "SharePoint", ParentID = 2 },
new FlatItem() { ID = 9, Title = "Office", ParentID = 2 },
new FlatItem() { ID = 10, Title = "Java", ParentID = 3 },
new FlatItem() { ID = 11, Title = "Word", ParentID = 9 },
new FlatItem() { ID = 12, Title = "Excel", ParentID = 9 },
new FlatItem() { ID = 13, Title = "PowerPoint", ParentID = 9 },
};
var lookup = source.ToLookup(x => x.ParentID);
Func<int?, List<NavigationItem>> build = null;
build = pid =>
lookup[pid]
.Select(x => new NavigationItem()
{
ID = x.ID,
Title = x.Title,
ParentID = x.ParentID,
Children = build(x.ID)
})
.ToList();
To start the process call build(null). That gives me this:
This does assume that the ParentId property is a int? - which your data table does suggest.
If you are ok with using recursion, you can create a function like this:
public List<NavigationItem> ChildrenOf(List<FlatItem> flatItems, int parentId)
{
var childrenFlatItems = flatItems.Where(i => i.ParentID == parentId);
return childrenFlatItems.Select(i => new NavigationItem {
ID = i.ID,
Title = i.Title,
ParentID = i.ParentID,
Children = ChildrenOf(flatItems, i.ID)})
.ToList();
}
Then, assuming that your root items have a parent id of 0 (since you aren't using nullable types), you generate the full list with:
ChildrenOf(flatsItems, 0)
Untested, however you could try this, should be fairly fast as well
var list = new List<FlatItem>();
var result = new List<NavigationItem>();
// just a helper to remember ids
var dict = new Dictionary<int, NavigationItem>();
foreach (var item in list)
{
var nav = new NavigationItem()
{
ID = item.ID,
ParentID = item.ParentID,
Title = item.Title,
Children = new List<NavigationItem>()
};
if (!dict.ContainsKey(nav.ParentID))
result.Add(nav);
else
dict[nav.ParentID].Children.Add(nav);
dict.Add(item.ID, nav);
}
no recursive, just GroupBy.
List<NavigationItem> list = ... // map from List<FlatItem>
// and init Children = new List<NavigationItem>();
var groups = list.GroupBy(x => x.ParentID).ToList();
foreach (var g in groups)
{
var items = list.Find(x => x.ID == g.Key);
if (items != null)
items.Children = g.ToList();
}
// tops is [Google, Microsoft, Oracle]
var tops = list.Where(x => x.ParentID == null).ToList();

create Expression tree in c# to select multiple records using LINQ.Expressions

For the following sample student list I want to select multiple records from the list using Expression tree(to generate dynamic LINQ query)
- 1 | John | 13
- 2 | Steve | 15
- 3 | Bill | 18
- 4 | Ram | 12
- 5 | Ron | 21
For selecting single record
SQL Query:
select * from studentList where StudentID = 2
LINQ:
var studentsData = studentList.Where(s=>s.StudentID == 2).AsQueryable();
likewise I need to create Expression tree for selecting multiple records from the list of values
For example SQL Query:
From the list of IDs I need create expression for selecting records
select * from studentList where StudentID in (2,4,3)
sample Output:
- 2 | Steve
- 4 | Ram
- 3 | Bill
Sample Expression tree Code:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 13 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 15 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 12 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 21 }
};
var studentwithLinQ = studentList.Where(s=>s.StudentID == 2);
foreach(var stu in studentwithLinQ)
Console.WriteLine("{0} | {1}",stu.StudentID, stu.StudentName);
ParameterExpression pe = Expression.Parameter(typeof(Student), "s");
MemberExpression me = Expression.Property(pe, "StudentID");
int id = 2;
ConstantExpression constant = Expression.Constant(id, typeof(int));
BinaryExpression body = Expression.Equal(me, constant);
Expression predicateExpression = Expression.Lambda(body, pe);
var sourcequery = studentList.AsQueryable();
Expression sourceExpression = Expression.Convert(Expression.Constant(sourcequery), typeof(IQueryable<Student>));
Expression filterExpressionExpression = Expression.Constant(predicateExpression);
var queryExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { typeof(Student)}, sourceExpression,filterExpressionExpression);
sourcequery = Expression.Lambda(queryExpression).Compile().DynamicInvoke() as IQueryable<Student>;
Console.WriteLine("sourceExpression: {0}", sourcequery);
var studentWithExpression = sourcequery;
foreach(var stu in studentWithExpression)
Console.WriteLine("{0} | {1}",stu.StudentID, stu.StudentName);
}
}
public class Student{
public int StudentID { get; set; }
public string StudentName { get; set; }
public int Age { get; set; }
}
I can create a Expression code for selecting single record. But i am not able to select the values using multiple records from datalist.can you please help me to select multiple values using Expression Statments.
I think trying to get it fixed with expressions it a hell of a job. These functionality can already be achieved with existing methods. (which keeps your solution much more simplistic) ;-)
You could try this:
// your original set
IList<Student> studentList = new List<Student>() {
new Student() { StudentID = 1, StudentName = "John", Age = 13 } ,
new Student() { StudentID = 2, StudentName = "Steve", Age = 15 } ,
new Student() { StudentID = 3, StudentName = "Bill", Age = 18 } ,
new Student() { StudentID = 4, StudentName = "Ram" , Age = 12 } ,
new Student() { StudentID = 5, StudentName = "Ron" , Age = 21 }
};
// create a subselection of ids only into an array(or list) of the id's only.
// results in `int[] { 1, 2, 3, 4, 5 };`
var ids = studentList.Select(student => student.StudentID).ToArray();
// use it on the studentList.
var studentwithLinQ = studentList.Where(s => ids.Contains(s.StudentID));
The ids array will be converted to an IN (..) statement.
You can create an expression tree for this, but that is doing things the hard way. Much simpler is to just create a list of the ids:
var ids = new List<int> { 2, 3, 4 };
and just use:
var filtered = studentList.Where(x => ids.Contains(x.StudentID));

Creating a tree from Parent/Child relation represented in a flat table

I have a C# List of following fields which are returned by a stored procedure:
CarrierId ParentCarrierId Name Descrition
1 NULL A AA
2 1 B BB
3 1 C CC
4 3 D DD
5 NULL E EE
I need to construct a nested object list out of this output
So each object of Carrier should have list of all it's children. Can anyone help me construct a LINQ code to accomplish this?
Desired Result:
CarrierId = 1
|__________________ CarrierId = 2
|
|__________________ CarrierId = 3
|
| |___________________ CarrierId = 4
CarrierId = 5
Desired result should be as mentioned above
the following code arrange things in tree but a child still appears in the list
c.Children = carrierList.Where(child => child.ParentCarrierId == c.CarrierId).ToList();
CarrierId = 1
|
|__________________ CarrierId = 2
|
|__________________ CarrierId = 3
| |___________________ CarrierId = 4
|
CarrierId = 2
|
CarrierId = 3
|
CarrierId = 4
|
CarrierId = 5
I don't want this behavior. If something appeared as Child it should be removed from root.
This is what you need.
First, start with the source data:
var source = new []
{
new { CarrierId = 1, ParentCarrierId = (int?)null, Name = "A", Description = "AA", },
new { CarrierId = 2, ParentCarrierId = (int?)1, Name = "B", Description = "BB", },
new { CarrierId = 3, ParentCarrierId = (int?)1, Name = "C", Description = "CC", },
new { CarrierId = 4, ParentCarrierId = (int?)3, Name = "D", Description = "DD", },
new { CarrierId = 5, ParentCarrierId = (int?)null, Name = "E", Description = "EE", },
};
Then, create a lookup by ParentCarrierId:
var lookup = source.ToLookup(x => x.ParentCarrierId);
Now we need an output structure:
public class Carrier
{
public int Id;
public List<Carrier> Children = new List<Carrier>();
public string Name;
public string Description;
}
Then, a build function to pop all of the carriers out by ParentCarrierId:
Func<int?, List<Carrier>> build = null;
build = pid =>
lookup[pid]
.Select(x => new Carrier()
{
Id = x.CarrierId,
Name = x.Name,
Description = x.Description,
Children = build(x.CarrierId),
})
.ToList();
NB: It's recursive so it needs to be defined with the initial = null.
Finally we build:
List<Carrier> trees = build(null);
This gives:

Select multiple fields group by and sum

I want to do a query with linq (list of objects) and I really don't know how to do it, I can do the group and the sum but can't select rest of the fields.
Example:
ID Value Name Category
1 5 Name1 Category1
1 7 Name1 Category1
2 1 Name2 Category2
3 6 Name3 Category3
3 2 Name3 Category3
I want to group by ID, SUM by Value and return all fields like this.
ID Value Name Category
1 12 Name1 Category1
2 1 Name2 Category2
3 8 Name3 Category3
Updated :
If you're trying to avoid grouping for all the fields, you can group just by Id:
data.GroupBy(d => d.Id)
.Select(
g => new
{
Key = g.Key,
Value = g.Sum(s => s.Value),
Name = g.First().Name,
Category = g.First().Category
});
But this code assumes that for each Id, the same Name and Category apply. If so, you should consider normalizing as #Aron suggests. It would imply keeping Id and Value in one class and moving Name, Category (and whichever other fields would be the same for the same Id) to another class, while also having the Id for reference. The normalization process reduces data redundancy and dependency.
void Main()
{
//Me being lazy in init
var foos = new []
{
new Foo { Id = 1, Value = 5},
new Foo { Id = 1, Value = 7},
new Foo { Id = 2, Value = 1},
new Foo { Id = 3, Value = 6},
new Foo { Id = 3, Value = 2},
};
foreach(var x in foos)
{
x.Name = "Name" + x.Id;
x.Category = "Category" + x.Id;
}
//end init.
var result = from x in foos
group x.Value by new { x.Id, x.Name, x.Category}
into g
select new { g.Key.Id, g.Key.Name, g.Key.Category, Value = g.Sum()};
Console.WriteLine(result);
}
// Define other methods and classes here
public class Foo
{
public int Id {get;set;}
public int Value {get;set;}
public string Name {get;set;}
public string Category {get;set;}
}
If your class is really long and you don't want to copy all the stuff, you can try something like this:
l.GroupBy(x => x.id).
Select(x => {
var ret = x.First();
ret.value = x.Sum(xt => xt.value);
return ret;
}).ToList();
With great power great responsibility comes. You need to be careful. Line ret.value = x.Sum(xt => xt.value) will change your original collection, as you are passing reference, not new object. If you want to avoid it, you need to add some Clone method into your class like MemberwiseClone (but again, this will create shallow copy, so be careful). Afer that just replace the line with: var ret = x.First().Clone();
try this:
var objList = new List<SampleObject>();
objList.Add(new SampleObject() { ID = 1, Value = 5, Name = "Name1", Category = "Catergory1"});
objList.Add(new SampleObject() { ID = 1, Value = 7, Name = "Name1", Category = "Catergory1"});
objList.Add(new SampleObject() { ID = 2, Value = 1, Name = "Name2", Category = "Catergory2"});
objList.Add(new SampleObject() { ID = 3, Value = 6, Name = "Name3", Category = "Catergory3"});
objList.Add(new SampleObject() { ID = 3, Value = 2, Name = "Name3", Category = "Catergory3"});
var newList = from val in objList
group val by new { val.ID, val.Name, val.Category } into grouped
select new SampleObject() { ID = grouped.ID, Value = grouped.Sum(), Name = grouped.Name, Category = grouped.Category };
to check with LINQPad:
newList.Dump();

Categories