Using Linq Aggregate to add elements of a List - c#

As I understand the Aggregate extension method, I should be able to use it to iterate through a collection and perform operations on the current and previous element; for example:
var totalSum = myIntCollection.Aggregate( (a,b) => a + b);
However, can (and if so how) can I use this to add class properties; for example:
totalSum = MyCollection.Aggregate((a, b) => a.MyInt + b.MyInt);
Given that MyCollection is a List<MyClass> and MyClass looks like this:
public class MyClass
{
public string Name { get; set; }
public int MyInt { get; set; }
}
When I try to compile the above statement, it tells me that it can't implicitly covert int to MyClass.
So, my question is: why is it giving this error, and is there a way around it?

You misunderstood on what Aggregate method operates. It does not "perform operations on the current and previous element".
It performs operations on an accumulator and current element.
So, in your first example var totalSum = myIntCollection.Aggregate( (a,b) => a + b);, a is not a previous element; it represents the sum of all elements that appear before current element (b). I think your misunderstanding is due to the fact that the Aggregate method in your case started by adding the first two elements.
Anyway, as #Sayse and #Jon Skeet pointed out, you're better of with using Sum in this case:
var totalSum = MyCollection.Sum(item => item.MyInt);

The way around it would be
MyCollection.Sum(a => a.MyInt)

Related

How to return List of objects containing aggregated (rising) sum of following elements with C# Enumerable.Aggregate method?

I have class ObjectA with property called Value.
I have also class ObjectB with property called Sum.
Then I have List<ObjectA> listA.
How to return List<ObjectB> listB from C# Enumerable.Aggregate method, where property Sum of every following ObjectB is the (rising) aggregated sum of properties Value from List<ObjectA>?
EXPECTED BEHAVIOUR:
IN: List of ObjectA with following properties Value {1,2,3,4,5,6}
OUT: List of ObjectB with following properties Sum {1,3,6,10,15,21}
Short answer: Don't. Use Select or a loop instead.
Long answer:
Aggregate is intended for cases where you start with a list of values, but want to end up with a single value. You're trying to end up with another collection, with one value for each input.
That's not to say it can't be done. Just for academic purposes, let's look at some of the patterns you could use. I'm going to simplify the problem to just use integers instead of ObjectA and ObjectB.
This is probably the most "pure" approach, in the sense that the delegate you pass to Aggregate has no side-effects.
var values = new[]{1,2,3,4,5,6};
var sums = values.Aggregate(Enumerable.Empty<int>(), (previous, next) => previous.Append(previous.LastOrDefault() + next)).ToList();
However, this probably has O(n²) complexity because you're calling LastOrDefault() on IEnumerable<>s constructed by chaining Append calls together. It might be preferable to accept a little impurity by closing over a running total variable.
var values = new[]{1,2,3,4,5,6};
int runningTotal = 0;
var sums = values.Aggregate(Enumerable.Empty<int>(), (previous, next) => previous.Append(runningTotal += next)).ToList();
But if we're willing to track state that way, things could be much simpler by just using Select instead of Aggregate:
int runningTotal = 0;
var sums = values.Select(next => runningTotal += next).ToList();
And of course, purists would say that functional syntax like LINQ statements shouldn't have side-effects. It might be clearer to just build out a new list with a more imperative-style foreach loop.
var values = new[]{1,2,3,4,5,6};
var sums = new List<int>();
int runningTotal = 0;
foreach(var next in values)
{
runningTotal += next;
sums.Add(runningTotal);
}
You can do the Aggregate in the following way:
var objectBs = objectAs
.Aggregate<ObjectA, IEnumerable<ObjectB>>(
Enumerable.Empty<ObjectB>(),
(objectBs, objectA) => objectBs.Append(new ObjectB
{
Sum = (objectBs.LastOrDefault()?.Sum ?? 0) + objectA.Value
}));
But this hard to read for people (I personally do). Alternatively, write it with a for loop:
var listOfObjectBs = new List<ObjectB>
{
new ObjectB
{
Sum = objectAs.First().Value
}
};
for (var i = 1; i < objectAs.Count; i++)
{
listOfObjectBs.Add(new ObjectB
{
Sum = listOfObjectBs[i - 1].Sum + objectAs[i].Value
});
}

IEnumerable Select statement with ternary operator

I have an odd behavior using an IEnumerable<string> with a ternary operator and a Select statement.
I have two lists with different objects. One list contains Enums the other list contains objects. Those objects do have a String property.
If one list is null or empty I want to get the values of the other list.
Here is some code:
public class ExportItem
{
public string Type;
...
}
public enum ExportType
{
ExportType1,
ExportType2,
...
}
The List<ExportItem> is always filled by a config file. The List<ExportType> is filled if command line arguments are provided. So if List<ExportType> is filled I want to use them, otherwise I want to use those from the config file.
So my code ist like this:
IEnumerable<string> exportTypes = MyListOfExportTypes != null &&
MyListOfExportTypes.Any() ? MyListOfExportTypes.Select(x => x.ToString()) :
MyListOfExportItems.Select(x => x.Type);
The thing is that exportTypes is null but I don't get it...
When I do this with if-else everything works as expected. Also if exportTypes is of type List<string> and I call ToList() after the Select statement everything works fine.
Using var a = MyListOfExportTypes.Select(x => x.ToString()); and var b = MyListOfExportItems.Select(x => x.Type); does work as expected.
Must be something with the ternary operator and/or IEnumerable. But what?
Or what do I miss? Any suggestions?
EDIT:
I now have a screenshot...
Note that the code above foreach works nevertheless...
Not sure if this was answered,
But I think that this is related to the fact that you are using LINQ deferred execution.
When writing LINQ queries,
there is a difference between creating the query and executing it.
Writing the select statement, is creating the query, adding ToList() executes it.
Think of it like writing SQL query in SQL server console (that's the writing stage),
and once you hit F5 (Or the play button) you execute it.
I hope this little code sample will help to clarify it.
public class SomeClass
{
public int X { get; set; }
public int Y { get; set; }
public void Test()
{
//Here I'm creating a List of Some class
var someClassItems = new List<SomeClass> {
new SomeClass { X = 1, Y = 1 },
new SomeClass { X = 2, Y = 2 }
};
//Here I'm creating a Query
//BUT, I'm not executing it, so the query variable, is represented by the IEnumerable object
//and refers to an in memory query
var query = someClassItems.
Select(o => o.X);
//Only once the below code is reached, the query is executed.
//Put a breakpoint in the query, click the mouse cursor inside the select parenthesis and hit F9
//You'll see the breakpoint is hit after this line.
var result = query.
ToList();
}
}

LINQ Replacement for Nested Array Iteration and Update?

I need some help with syntactic sugar.
I have a ThisClass[3] and ThatClass[3].
public class ThisClass
{
public string Thing1;
public string Thing2;
public string Thing3;
public string Thing4;
}
public class ThatClass
{
public string Thing1;
public string Thing2;
}
Each instance in the array of ThatClass was created based on an instance in the same position of array ThisClass.
So ThatClass[0] has its fields with the same values as ThisClass[0], except it only has 2 fields instead of 4.
I would like to now update each instance in the ThisClass array, with fields from the matching index position of the object in the ThatClass array. I could do nested for loops, but I need help thinking through a LINQ option.
ThisClass[0].Thing1 = ThatClass[0].Thing1;
ThisClass[0].Thing2 = ThatClass[0].Thing2;
works but I am sure could be done better. Using C#, .NET 4.5.
I don't see any need for nested loops:
for (int i = 0; i < theseClasses.Length; i++)
{
theseClasses[i].Thing1 = thoseClasses[i].Thing1;
theseClasses[i].Thing2 = thoseClasses[i].Thing2;
}
You could potentially add a CopyFrom(ThatClass) method to ThisClass, leading to:
for (int i = 0; i < theseClasses.Length; i++)
{
theseClasses[i].CopyFrom(thoseClasses[i]);
}
... but that's all I'd do. LINQ is do to with querying, not causing side-effects... I don't think it's a good fit here.
Attention: As #Jon put, LINQ is not about causing side-effects and if you do so you may end up with a code with unexpected behavior (but it's possible).
This code does that:
ThisClass[] these = new ThisClass[100];
ThatClass[] those = new ThatClass[100];
// init these and those items
those.Zip(these, (that, #this) =>
{
#this.Thing1 = that.Thing1;
#this.Thing2 = that.Thing2;
return that;
}).ToList();
As you're asking for LINQ... this will get you an unrelated IEnumerable<ThisClass>, and will not modify the original array.
(I'm assuming that the thisClass and thatClass arrays are called thisArray and thatArray, respectively)
thisArray.Select((n, x) => { n.Thing1 = thatArray[x].Thing1; n.Thing2 = thatArray[x].Thing2; return n; }).ToArray();
(If you really wanted LINQ and assigning it, just assign it back to the original array)

c# looping object creation

I'm very new with c#, and was previously attempting to ignore classes and build my small program structurally more similar to PHP. After reaching a road block, I'm trying to start over and approach the problem properly OO. I'm taking a long file, and in a loop, every time certain conditions are met, I want to make a new object. How can I have it create a new object, without having to specify a unique name?
Referral ObjectName = new Referral(string, string, int);
Secondly, once this is done, and the strings & int set their appropriate object properties, how can i unique-ify the class by one property, and then sort the class by another?
I'm sorry if these are basic questions, I have spent a large, large amount of time first trying to figure it out on my own with google, and a textbook. If only C# would allow multi-dimensional arrays with different types!
Thank you so much!
PS. I do mean to extract a list of unique objects.
All these answers, while helpful, seem to involve creating a shadow set of IEnumerables. Is there no way to do this with the class itself?
Trying the first solution, provided by Earwicker, adding each object to a List from within the loop, when I try to Write a property of the element to the console, i get the ClassName+Referral. What could I be doing wrong?--solved. still needed .property
still working. . .
C# does allow untyped arrays. All objects are derived ultimately from object, so you use an array or container of objects. But it's rarely necessary. How many types of object do you have?
Within the loop block, you can create an object exactly as you do in that line of code (except with the syntax fixed), and it will be a new object each time around the loop. To keep all the objects available outside the loop, you would add it to a container:
List<Referral> referrals = new List<Referral>();
// in the loop:
Referral r = new Referral(str1, str2, num1);
referrals.Add(r);
Suppose Referral has a numeric property called Cost.
referrals.Sort((l, r) => l.Cost - r.Cost);
That sorts by the cost.
For ensuring uniqueness by some key, you may find it easier to pick a more suitable container.
Dictionary<string, Referral> referrals = new List<Referral>();
// in the loop:
Referral r = new Referral(str1, str2, num1);
referrals[str1] = r;
This stores the referral in a "slot" named after the value of str1. Duplicates will overwrite each other silently.
First, you're going to need to spend some time familiarizing yourself with the basics of the language to be productive. I recommend you take a little time to read up on C# before getting in too deep - otherwise you'll spend a lot of your time spinning your wheels - or reinventing them :)
But here's some info to get you started.
Typically, in C# you create classes to represent elements of your program - including those that are used to represent information (data) that your program intends to manipulate. You should really consider using one, as it will make data manipulation clearer and more manageable. I would advise avoiding untyped, multi-dimensions array structures as some may suggest, as these rapidly become very difficult to work with.
You can easily create a Referall class in C# using automatic properties and a simple constructor:
public class Referall
{
// these should be named in line with what they represent...
public string FirstString { get; set; }
public string AnotherString { get; set; }
public int SomeValue { get; set; }
public Referall( string first, string another, int value )
{
FirstString = first;
AnotherString = another;
SomeValue = value;
}
}
You can add these to a dictionary as you create them - the dictionary can be keyed by which ever property is unique. Dictionaries allow you to store objects based on a unique key:
Dictionary<string,Referall> dict = new Dictionary<string,Referall>();
As you process items, you can add them to the dictionary:
Referall ref = new Referall( v1, v2, v3 );
// add to the dictionary, keying on FirstString...
dict.Add( ref.FirstString, ref );
If you need to sort items in the dictionary when you're done, you can use LINQ in C# 3.0:
IEnumerable<Referall> sortedResults =
dict.Values.OrderBy( x => x.AnotherString );
You can sort by multiple dimension using ThenBy() as well:
IEnumerable<Referall> sortedResults =
dict.Values.OrderBy( x => x.AnotherString )
.ThenBy( x => x.SomeValue );
List<Referral> referrals = new List<Referral>();
for (...)
{
referrals.Add(new Referral(string1, string2, number1));
}
Then, if you're using Linq (which I highly suggest), you can do this:
IEnumerable<Referral> sorted = referrals.OrderBy(x => x.string1).ThenBy(x => x.string2);
Otherwise, you can use the Sort() method on List<Referral>.
You can create an object without a reference, but you won't have any access to it later:
new Referral(string, string, int);
If you wish to put them in an array/list, these different types need to have a common base class. This is called polimorfism, which is a very important concept in OO programming.
You cannot ignore classes while using c#. Don't resist the change!
Do you really not need to create a class here? Do you really not need to give it a name? C# does allow loose typing, but type safety is a good thing.
I don't fully understand what you're trying to do. But maybe LINQ is what you're looking for. There's tons of documentation around, but as a quick 'teaser' have a look at the 101 Linq samples on MSDN
C# includes a wonderful feature called "iterator blocks". What you want to do is use the yield keyword to create an Enumerable of your Referal object, something like this (not that I'm making the file format and property names up, because you didn't share that):
public class Referral
{
public Guid id { get; private set; } // "uniquify"
public int ReferringId { get; set; }
public string ReferrerText { get; set; }
public string ReferrerDescription { get; set; }
private Referral()
{
id = new Guid();
}
private Referral(string Text, string Description, int ReferringId) : this()
{
this.ReferrerText = Text;
this.ReferrerDescription = Description;
this.ReferringId = ReferringId;
}
public static IEnumerable<Referral> GetReferrals(string fileName)
{
using (var rdr = new StreamReader(fileName))
{
var next = new Referrer();
int state = 0;
string line;
while ( (line = rdr.ReadLine() ) != null)
{
switch (state)
{
case 0:
next.ReferrerText = line;
state = 1;
break;
case 1:
next.ReferrerDescription = line;
state = 2;
break;
case 2:
next.ReferringId = int.Parse(line);
yield return next;
next = new Referral();
state = 0;
break;
}
}
}
}
}
Now you want to sort the referrals and presumable enumerate over them for some purpose. You can do that easily like this:
foreach (var referral in Referral.GetReferrals(#"C:\referralfile.txt").OrderBy( r => r.Text ) )
{
OutputReferral(referral);
}

c# modifying structs in a List<T>

Short question: How can I modify individual items in a List? (or more precisely, members of a struct stored in a List?)
Full explanation:
First, the struct definitions used below:
public struct itemInfo
{
...(Strings, Chars, boring)...
public String nameStr;
...(you get the idea, nothing fancy)...
public String subNum; //BTW this is the element I'm trying to sort on
}
public struct slotInfo
{
public Char catID;
public String sortName;
public Bitmap mainIcon;
public IList<itemInfo> subItems;
}
public struct catInfo
{
public Char catID;
public String catDesc;
public IList<slotInfo> items;
public int numItems;
}
catInfo[] gAllCats = new catInfo[31];
gAllCats is populated on load, and so on down the line as the program runs.
The issue arises when I want to sort the itemInfo objects in the subItems array.
I'm using LINQ to do this (because there doesn't seem to be any other reasonable way to sort lists of a non-builtin type).
So here's what I have:
foreach (slotInfo sInf in gAllCats[c].items)
{
var sortedSubItems =
from itemInfo iInf in sInf.subItems
orderby iInf.subNum ascending
select iInf;
IList<itemInfo> sortedSubTemp = new List<itemInfo();
foreach (itemInfo iInf in sortedSubItems)
{
sortedSubTemp.Add(iInf);
}
sInf.subItems.Clear();
sInf.subItems = sortedSubTemp; // ERROR: see below
}
The error is, "Cannot modify members of 'sInf' because it is a 'foreach iteration variable'".
a, this restriction makes no sense; isn't that a primary use of the foreach construct?
b, (also out of spite) what does Clear() do if not modify the list? (BTW, the List does get cleared, according to the debugger, if I remove the last line and run it.)
So I tried to take a different approach, and see if it worked using a regular for loop. (Apparently, this is only allowable because gAllCats[c].items is actually an IList; I don't think it will allow you to index a regular List this way.)
for (int s = 0; s < gAllCats[c].items.Count; s++)
{
var sortedSubItems =
from itemInfo iInf in gAllCats[c].items[s].subItems
orderby iInf.subNum ascending
select iInf;
IList<itemInfo> sortedSubTemp = new List<itemInfo>();
foreach (itemInfo iInf in sortedSubItems)
{
sortedSubTemp.Add(iInf);
}
//NOTE: the following two lines were incorrect in the original post
gAllCats[c].items[s].subItems.Clear();
gAllCats[c].items[s].subItems = sortedSubTemp; // ERROR: see below
}
This time, the error is, "Cannot modify the return value of 'System.Collections.Generic.IList.this[int]' because it is not a variable." Ugh! What is it, if not a variable? and when did it become a 'return value'?
I know there has to be a 'correct' way to do this; I'm coming to this from a C background and I know I could do it in C (albeit with a good bit of manual memory management.)
I searched around, and it seems that ArrayList has gone out of fashion in favor of generic types (I'm using 3.0) and I can't use an array since the size needs to be dynamic.
Looking at the for-loop approach, the reason (and solution) for this is given in the documentation for the compilation error:
An attempt was made to modify a value
type that is produced as the result of
an intermediate expression but is not
stored in a variable. This error can
occur when you attempt to directly
modify a struct in a generic
collection.
To modify the struct, first assign it
to a local variable, modify the
variable, then assign the variable
back to the item in the collection.
So, in your for-loop, change the following lines:
catSlots[s].subItems.Clear();
catSlots[s].subItems = sortedSubTemp; // ERROR: see below
...into:
slotInfo tempSlot = gAllCats[0].items[s];
tempSlot.subItems = sortedSubTemp;
gAllCats[0].items[s] = tempSlot;
I removed the call to the Clear method, since I don't think it adds anything.
The problem you are having in your foreach is that structs are value types, and as a result, the loop iteration variable isn't actually a reference to the struct in the list, but rather a copy of the struct.
My guess would be the compiler is forbidding you change it because it most likely would not do what you expect it to anyway.
subItems.Clear() is less of a problem, because altho the field may be a copy of the element in the list, it is also a reference to the list (shallow copy).
The simplest solution would probably be to change from a struct to a class for this. Or use a completely different approach with a for (int ix = 0; ix < ...; ix++), etc.
The foreach loop doesn't work because sInf is a copy of the struct inside items. Changing sInf will not change the "actual" struct in the list.
Clear works because you aren't changing sInf, you are changing the list inside sInf, and Ilist<T> will always be a reference type.
The same thing happens when you use the indexing operator on IList<T> - it returns a copy instead of the actual struct. If the compiler did allow catSlots[s].subItems = sortedSubTemp;, you'll be modifying the subItems of the copy, not the actual struct. Now you see why the compiler says the return value is not a variable - the copy cannot be referenced again.
There is a rather simple fix - operate on the copy, and then overwrite the original struct with your copy.
for (int s = 0; s < gAllCats[c].items.Count; s++)
{
var sortedSubItems =
from itemInfo iInf in gAllCats[c].items[s].subItems
orderby iInf.subNum ascending
select iInf;
IList<itemInfo> sortedSubTemp = new List<itemInfo>();
foreach (itemInfo iInf in sortedSubItems)
{
sortedSubTemp.Add(iInf);
}
var temp = catSlots[s];
temp.subItems = sortedSubTemp;
catSlots[s] = temp;
}
Yes, this results in two copy operations, but that's the price you pay for value semantics.
The two errors you specified have to do with the fact that you are using structs, which in C# are value types, not reference types.
You absolutely can use reference types in foreach loops. If you change your structs to classes, you can simply do this:
foreach(var item in gAllCats[c].items)
{
item.subItems = item.subItems.OrderBy(x => x.subNum).ToList();
}
With structs this would need to change to:
for(int i=0; i< gAllCats[c].items.Count; i++)
{
var newitem = gAllCats[c].items[i];
newitem.subItems = newitem.subItems.OrderBy(x => x.subNum).ToList();
gAllCats[c].items[i] = newitem;
}
The other answers have better information on why structs work different than classes, but I thought I could help with the sorting part.
If subItems was changed to a concrete List instead of the interface IList, then you'd be able to use the Sort method.
public List<itemInfo> subItems;
So your whole loop becomes:
foreach (slotInfo sInf in gAllCats[c].items)
sInf.subItems.Sort();
This won't require the contents of the struct to be modified at all (generally a good thing). The struct's members will still point to exactly the same objects.
Also, there are very few good reasons to use struct in C#. The GC is very, very good, and you'd be better off with class until you've demonstrated a memory allocation bottleneck in a profiler.
Even more succinctly, if items in gAllCats[c].items is also a List, you can write:
gAllCats[c].items.ForEach(i => i.subItems.Sort());
Edit: you give up too easily! :)
Sort is very easy to customise. For example:
var simpsons = new[]
{
new {Name = "Homer", Age = 37},
new {Name = "Bart", Age = 10},
new {Name = "Marge", Age = 36},
new {Name = "Grandpa", Age = int.MaxValue},
new {Name = "Lisa", Age = 8}
}
.ToList();
simpsons.Sort((a, b) => a.Age - b.Age);
That sorts from youngest to oldest. (Isn't the type inference good in C# 3?)

Categories