Sum List<T> Properties of each item in List<T> - c#

Please refer to ff. codes:
MainObj:
public class MainObj {
public string Name { get; set; }
public int SomeProperty { get; set; }
public List<SubObj> Subojects { get; set; }
}
SubObj:
public class SubObj {
public string Description { get; set; }
public decimal Value { get; set; }
}
QUESTION: I have a List<MainObj>. Each item has equal number of SubObj. How do I sum the values of each SubObj from MainObj A which corresponds to the same index as the SubObj in the other MainObj in the list.
To illustrate further:
Results:
{
Name: "something....",
Value: SUM(MainObj_A.SubObj[0], MainObj_B.SubObj[0] .... MainObj_n.SubObj[0]
},
{
Name: "don't really care...."
Value: SUM(MainObj_A.SubObj[1], MainObj_B.SubObj[1] .... MainObj_n.SubObj[1]
},
{
Name: "don't really care...."
Value: SUM(MainObj_A.SubObj[2], MainObj_B.SubObj[2] .... MainObj_n.SubObj[2]
}
.... so on and so forth
I know I can loop thru each item in MainObj then perform the sum but I was hoping that there is a simpler way using Linq.
Sorry if I can't put the right words for my question. I hope the illustrations helped.
Every help would be much appreciated. CHEERS!

As I understand the question you look for something like this:
var results = list.Select((m, i) => new
{
m.Name,
Value = list.Sum(t => t.SubObj[i].Value)
}).ToList();
This creates a list of an anonymous type containing the name of each main object and the sum of all subobject values with the same index as the main object.
It's not clear from your question, but if you want to sum all subojects (not only as many as there are main objects), you can do it like this:
var results = Enumerable.Range(0, list[0].SubObj.Count)
.Select(i => list.Sum(m => m.SubObj[i].Value)).ToList();
This gives you a list of the sums (without names, as your examples suggest that you "don't really care" about any names).

For flexibility and slightly boosted performance, I'd prefer a mixed LINQ-sql way as shown in my answer below.
var result = from x in list
where x...
group x by x.SomeProperty
into item
select new {
Id = item.Key,
Name = item.Name,
Value = item.SubObj.Sum(i => i.Value)
};

I would extend solution of Rene, to sum over all subitems:
var result = list.First().Subojects.Select((m, i) => new
{
Name = i.ToString(),
Value = list.Sum(t => t.Subojects[i].Value)
}).ToList();
i have used it with this values:
var list = new List<MainObj>
{
new MainObj { Name = "A", Subojects = new List<SubObj>{new SubObj{ Value = 1}, new SubObj{ Value = 2}, new SubObj{ Value = 3}}},
new MainObj { Name = "B", Subojects = new List<SubObj>{new SubObj{ Value = 1}, new SubObj{ Value = 2}, new SubObj{ Value = 3}}}
};
And result is:
0:2
1:4
2:6

Related

Add element to a List when delcaring new object

Is there a way to add elements to a List when doing this:
var Foo = new MyClass() {
PropertyList = MyList,
Id = Id,
}
I would like to add elements to PropertyList. For example would be the same as: MyList.Add()
The problem is that i do not have a list called MyList but i rather have elements that i want to append to PropertyList
Updating code based on comments:
var result1 = await query
.GroupBy(c => new {
c.CommissionId, c.ActivityId
})
.Select(grp => new RegistrationStatisticViewModel() {
CommissionId = grp.Key.CommissionId,
CommissionCode = grp.First().Commission.Code,
CommissionDescription = grp.First().Commission.Description,
MinuteWorked = grp.Sum(c => c.MinuteWorked),
ActivityId = grp.Key.ActivityId,
ActivityCode = grp.First().Activity.Code,
ActivityDescription = grp.First().Activity.Description,
})
.ToListAsync();
var grps = from d in result1
group d by d.CommissionId
into grp
select new RegistrationStatisticViewModel() {
CommissionId = grp.Key,
ActivityList = new List < Activity > {
new Activity {
//ActivityId = grp.Select(d => d.ActivityId),
//Code = grp.Select(d => d.ActivityCode),
//Description = grp.Select(d => d.ActivityDescription),
}
},
CommissionCode = grp.First().CommissionCode,
CommissionDescription = grp.First().CommissionDescription,
MinuteWorked = grp.First().MinuteWorked
};
return grps;
To give context:
forget the result1 is just some data i retrieve from my database
Commission is one class and contains:
CommissionId
Code
Description
Activity is one class and contains:
ActivityId ==> type GUID
Code ==> type string
Description ==> type string
Now the var = grps is a LINQ that gets the data and then instatiates a new object (class) new RegistrationStatisticViewModel()
So the tricky part we were discussing before is when i populate ActivityList with multiple activities.
When populating the list if i use .First() or .Select() i would only get one instance and therfore the list would only have one activity.
It worked when using .ToArray() for example if i replace ActivityList with just the ActivityId of type string (so a new property on RegistrationStatisticViewModel that is not a list anymore):
I can do this ActivityId = grp.Select(d2 => d2.ActivityId).ToArray()
And it will give me an array of all the ActivityId linked to that commissionId
I am sorry if this is confusing but it is for me as well. I would thank you if you could help me. No worries if you can't you have already give me very helpful answers, so i thank you for that!
Based on your remarks, I believe this is what you are trying to achieve:
public class PersonContainer
{
public IList<Person> Persons { get; set; }
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
var personContainer = new PersonContainer
{
Persons = new List<Person>
{
new Person
{
Name = "John Doe",
Age = 28,
},
new Person
{
Name = "Jane Doe",
Age = 27,
},
}
};
Here, the Persons property of PersonContainer is initialized and populated with Person elements during instantiation.

list compare and replace on matching condition linq C#

public class UserValues
{
public string UserId { get; set; }
public int FieldId { get; set; }
public string FieldValue { get; set; }
}
public class LookupMeta
{
public int FieldId { get; set; }
public int Id { get; set; }
public int FieldValueId { get; set; }
public string Title { get; set; }
}
I have kept this in 2 different lists after reading it from DB.
Now I want to compare both the list with
FieldId == FieldId
and FieldValue equals Id
then replace FieldValue from uservalues to FieldValueId from lookupMeta
UserValues
.Where(x => LookupMeta.Any(y =>
y.FieldId == x.FieldId &&
y.FieldValueId.Equals(x.FieldValue)))
.Select(x => x.FieldValue.Replace(x.FieldValue, ???))
I am looking at this link as well. I am struck C# LINQ code for two list compare and replace
Is it good to have in List and doing like this or is there any other optimized way?
Based on the comment that has been left on pwilcox's answer it seems like the OP is look for a solution where the unmatched rows are also included. That means instead of using inner join we are looking for a left outer join.
In the world of Linq this could be achieved via a combination of GroupJoin, SelectMany and Select operators.
In order to be able to join on two different columns we have to introduce an intermediate class to be able to tell the types of the GroupJoin. So, I have created the following class:
internal class IntermediateKey
{
public int Id { get; set; }
public string Value { get; set; }
}
We also have to define a comparer for this class to be able to find matching data:
internal class IntermediateKeyComparer : IEqualityComparer<IntermediateKey>
{
public bool Equals(IntermediateKey x, IntermediateKey y)
{
return x.Id == y.Id && x.Value == y.Value;
}
public int GetHashCode(IntermediateKey obj)
{
return obj.Id.GetHashCode() + obj.Value.GetHashCode();
}
}
Please bear in mind that this implementation is quite simplified. The correct way to implement it is shown in this thread.
Now can define our query as this:
var comparer = new IntermediateKeyComparer();
var result = userValues
.GroupJoin(
lookupMetas,
uv => new IntermediateKey { Id = uv.FieldId, Value = uv.FieldValue },
lm => new IntermediateKey { Id = lm.FieldId, Value = lm.Id.ToString() },
(uv, lm) => new { Value = uv, Lookups = lm},
comparer)
.SelectMany(
pair => pair.Lookups.DefaultIfEmpty(),
(paired, meta) => new { Value = paired.Value, Lookup = meta})
.Select(res =>
{
res.Value.FieldValue = res.Lookup?.FieldValueId.ToString() ?? res.Value.FieldValue;
return res.Value;
});
We defined that userValues should be left outer joined on lookupMetas
if uv's FieldId is matches to lm's FieldId
and if uv's FieldValue is matches to lm's Id's string representation
With the SelectMany we choose either the matching LookupMeta entity or null
With the Select we update the UserValue's FieldValue property only if there is a related LookupMeta otherwise we use its original value.
Now let's see how this works with some sample data:
static void Main(string[] args)
{
var userValues = new List<UserValue>
{
new UserValue { FieldId = 1, FieldValue = "2"},
new UserValue { FieldId = 2, FieldValue = "3"},
new UserValue { FieldId = 4, FieldValue = "5"}
};
var lookupMetas = new List<LookupMeta>
{
new LookupMeta { FieldId = 1, Id = 2, FieldValueId = 20 },
new LookupMeta { FieldId = 2, Id = 3, FieldValueId = 30 },
new LookupMeta { FieldId = 3, Id = 4, FieldValueId = 40 },
};
var comparer = new IntermediateKeyComparer();
var result = userValues
.GroupJoin(
lookupMetas,
uv => new IntermediateKey { Id = uv.FieldId, Value = uv.FieldValue },
lm => new IntermediateKey { Id = lm.FieldId, Value = lm.Id.ToString() },
(uv, lm) => new { Value = uv, Lookups = lm},
comparer)
.SelectMany(
pair => pair.Lookups.DefaultIfEmpty(),
(x, meta) => new { Value = x.Value, Lookup = meta})
.Select(res =>
{
res.Value.FieldValue = res.Lookup?.FieldValueId.ToString() ?? res.Value.FieldValue;
return res.Value;
});
foreach (var maybeUpdatedUserValue in result)
{
Console.WriteLine($"{maybeUpdatedUserValue.FieldId}: {maybeUpdatedUserValue.FieldValue}");
}
}
The output will be:
1: 20
2: 30
4: 5
So, as you can see there is no matching LookupMeta for the last UserValue that's why its FieldValue remained intact.
If I follow you correctly, then the .Join() method in LINQ may be of use to you. Here I use it to accomplish what I think you're after.
UserValues
.Join(
LookupMeta,
uv => new { uv.FieldId, uv.FieldValue },
lm => new { lm.FieldId, lm.FieldValueId },
(uv,lm) => {
uv.FieldValue = lm.FieldValueId;
return uv;
}
);
The second and third lines in the method build anonymous objects from the source tables. The values of these are matched to make a link.
The last line takes the joined entries as inputs and then gives your output. In your case, I just return the UserValues entry. But before I do I change its "FieldValue" property to the "FieldValueId" property of the LookupMeta entry.
You have some inconsistencies. For instance, you talk about matching FieldValue to Id in the paragraph, but in the code you match FieldValue to FieldValueId. Also, you use == in one comparison and .Equals() in the other. No wrong answer here. I just don't know your underlying objects. So you may have to modify my code a bit to get what you want. But it shows the general strategy that I hope will work for you.

LINQ: Getting item from dictionary by key and also its values then assign them to variable

In Parts class we have Data dictionary that contains key "Number" and value "1" for example. The key is always called "Number" and the value is always string of some number 1,2,3 etc. I want to assign to one variable (List) all items that has the key "number" with their values and then to group them by the id in Parts. So in the end the result should be the Id from Parts, Number and its value.
public class People
{
public List<Parts> Parts { get; set; }
}
public class Parts
{
public string Name {get;set;}
public string Id {get;set;}
public Dictionary<string,string> Data {get;set}
}
var msf = new People();
Currently my example that does not work properly with linq :
var temp = msf
.Parts
.Select(s => s.Data.Keys.Where(key => key.Contains("Number"))
.ToList()
.Select(s = > s.Value));
Can someone give me better solution for this scenario code with linq?
"People":[
"id":"1234567"
"Parts":[
"id":"234567",
"name":"Lqlq"
"Data":{
"number" : "1"
}
"id":"3424242",
"name":"Lqlq2"
"Data":{
"number" : "2"
}
]
]
This should give you a Dictionary<string, List<string>> containing a list of ID strings for each "Number" value:
var idsByNumber = msf.Parts.Where(p => p.Data.ContainsKey("number")) // filter for all that have a number
.Select(p => new { ID = p.ID, Number = p.Data["number"] }) // select ID and the number value
.GroupBy(x => x.Number) // group by number
.ToDictionary(g => g.Key, g => g.ToList()); // create dictionary number -> id list
Here's an alternative syntax.
var temp = from part in msf.Parts
where part.Data["Number"] == "2"
select part;
Usually is a good idea to ask your questions using an MCVE - here's some code that can be pasted in Linqpad:
void Main()
{
var msf = new People() {
Parts = new List<Parts> {
new Parts { Name = "Lqlq", Id = "234567", Data = new Dictionary<string, string> { { "Number", "1"} } },
new Parts { Name = "Lqlq2", Id = "3424242", Data = new Dictionary<string, string> { { "Number", "2"} } },
}
};
var temp = from part in msf.Parts
where part.Data["Number"] == "2"
select part
;
temp.Dump();
}
public class People
{
public List<Parts> Parts { get; set; }
}
public class Parts
{
public string Name { get; set; }
public string Id { get; set; }
public Dictionary<string, string> Data { get; set; }
}

Getting the latest entry of each user in LINQ to Entities in a strongly-typed manner

I have a database of users and their check-ins in different places with timestamps, related with a foreign key. A user can check-in any time, and can have arbitrary number of entries. I need a LINQ-to-Entities query that will return the checkins in the database, but return only the latest check-in for each user. I am not really a master of SQL or LINQ grouping, I think I need to group the entries. I've seen LINQ group by and getting latest value. There is an answer (https://stackoverflow.com/a/2657436/811405) that returns what I'm trying to get, but it returns an anonymous class. Is there any way to return the instances of my class in a strongly typed manner without selecting an anonymous class?
UPDATE:
I already have this class:
public partial class LocationUpdate
{
public int ID { get; set; }
public System.DateTime DateUpdated { get; set; }
public System.Data.Entity.Spatial.DbGeography Position { get; set; }
public int UserID { get; set; }
public virtual User User { get; set; }
}
And I have this query:
IQueryable<LocationUpdate> nearbyUserLocations = [some LINQ-to-Entities query];
I want, just below that line, a query like this:
nearbyUserLocations = [collection of each user's latest location update];
I need it still in the type of IQueryable<LocationUpdate>, not some anonymous type.
Yes - if I am reading your question correctly, from your example - instead of doing:
var maxObjects =
from o in myList
group o by o.Name into g
select new { Name = g.Key, Created = g.Max(o => o.Created) };
try doing:
var maxObjects =
from o in myList
group o by o.Name into g
select new MyClass{ Name = g.Key, Created = g.Max(o => o.Created) };
and if you want queryable
maxObjects.AsQueryable<MyClass>();
Assuming that MyClass has public properties Name and Created.
Edit after further information
Here is a testable query...
class Program
{
static void Main(string[] args)
{
List<LocationUpdate> locationUpdates =
new List<LocationUpdate>
{
new LocationUpdate {UserID = 1, Position = 2},
new LocationUpdate {UserID = 1, Position = 3},
new LocationUpdate {UserID = 2, Position = 1},
new LocationUpdate {UserID = 2, Position = 2},
new LocationUpdate {UserID = 1, Position = 4},
new LocationUpdate {UserID = 3, Position = 1}
};
IEnumerable<Tuple<int, List<MyClass>>> result = locationUpdates.GroupBy(x => x.UserID)
.Select(x => new Tuple<int, List<MyClass>>(x.Key,
x.Select(y => new MyClass {Position = y.Position, UserID = y.UserID}).ToList()));
foreach (Tuple<int, List<MyClass>> tuple in result)
{
Console.WriteLine("User {0}", tuple.Item1);
foreach (MyClass myClass in tuple.Item2)
Console.WriteLine("User {0}, Position {1}", myClass.UserID, myClass.Position);
}
Console.ReadLine();
}
public class MyClass
{
public int Position { get; set; }
public int UserID { get; set; }
}
public class LocationUpdate
{
public int Position { get; set; }
public int UserID { get; set; }
}
}
Obviously I have left some of the extra properties off as they were just noise, but you should just be able to add them in the block of code which creates a new MyClass...
You can still make the result queryable - the result contains a grouping which may not be exactly what you were expecting - I think that maybe this result type will fit your needs though, and maybe that's why you couldn't get a suitable Linq query to get you data.
Whether or not it answers your question, I hope this helps!

Convert List<T> to Dictionary with strategy

I have List < DTO > where DTO class looks like this,
private class DTO
{
public string Name { get; set; }
public int Count { get; set; }
}
I create objects and add it to List.
var dto1 = new DTO { Name = "test", Count = 2 };
var dto2 = new DTO { Name = "test", Count = 3 };
var dtoCollection = new List<DTO> {dto1, dto2};
Now my requirement is I need to create an Dictionary from the dtoCollection whose Key is Name field and value is Sum of Count fields.
For example, if you convert the above dtoCollection to Dictionary, the entry should be like
Key = "test" , Value = "5"
Where Value is obtained from summing up the Count fields whose Name fields are same
I guess this will do what you need:
dtoCollection.GroupBy(x => x.Name).ToDictionary(x => x.Key, x => x.Sum(y => y.Count))
Following is the linq query
var dict = dtoCollection.GroupBy(x=>x.Name).ToDictionary(x=>x.Key, x=>x.Select(y=>y.Count).Sum());

Categories