I am trying to process some list with a functional approach in C#.
The idea is that I have a collection of Tuple<T,double> and I want to change the Item 2 of some element T.
The functional way to do so, as data is immutable, is to take the list, filter for all elements where the element is different from the one to change, and the append a new tuple with the new values.
My problem is that I do not know how to append the element at the end. I would like to do:
public List<Tuple<T,double>> Replace(List<Tuple<T,double>> collection, T term,double value)
{
return collection.Where(x=>!x.Item1.Equals(term)).Append(Tuple.Create(term,value));
}
But there is no Append method. Is there something else?
I believe you are looking for the Concat operator.
It joins two IEnumerable<T> together, so you can create one with a single item to join.
public List<Tuple<T,double>> Replace(List<Tuple<T,double>> collection, T term,double value)
{
var newItem = new List<Tuple<T,double>>();
newItem.Add(new Tuple<T,double>(term,value));
return collection.Where(x=>!x.Item1.Equals(term)).Concat(newItem).ToList();
}
It seems that .NET 4.7.1 adds Append LINQ operator, which is exactly what you want. Unlike Concat it takes a single value.
By the way, if you declare a generic method you should include type parameter(s) after its name:
public List<Tuple<T, double>> Replace<T>(List<Tuple<T, double>> collection, T term, double value)
{
return collection.Where(x => !x.Item1.Equals(term))
.Append(Tuple.Create(term, value))
.ToList();
}
LINQ is not for mutation.
Functional programming avoid mutation.
Thus:
public IEnumerable<Tuple<T,double>> Extend(IEnumerable<Tuple<T,double>> collection,
T term,double value)
{
foreach (var x in collection.Where(x=>!x.Item1.Equals(term)))
{
yield return x;
}
yield return Tuple.Create(term,value);
}
If you're willing to use an additional package, check out MoreLinq, available on Nuget. This provides a new overload to the Concat-Function:
public static IEnumerable<T> Concat<T>(this IEnumerable<T> head, T tail);
This function does exactly what was asked for, e.g. you could do
var myEnumerable = Enumerable.Range(10, 3); // Enumerable of values 10, 11, 12
var newEnumerable = myEnumerable.Concat(3); // Enumerable of values 10, 11, 12, 3
And, if you like LINQ, you will probably like a lot of the other new functions, too!
Additionally, as pointed out in a discussion on the MoreLinq Github-page, the function
public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> source, TSource element);
with a different name but the same functionality is available in .NET Core, so it might be possible that we will see it for C# in the future.
This should do what you want (although it uses mutation inside, it feels functional from a callers perspective):
public List<Tuple<T, double>> Replace(List<Tuple<T, double>> collection, T term, double value) {
var result = collection.Where(x => !x.Item1.Equals(term)).ToList();
result.Add(Tuple.Create(term, value));
return result;
}
A alternative way to do it is to use "map" (select in LINQ):
public List<Tuple<T, double>> Replace(List<Tuple<T, double>> collection, T term, double value) {
return collection.Select(x =>
Tuple.Create(
x.Item1,
x.Item1.Equals(term) ? value : x.Item2)).ToList();
}
But it might give you different results than your original intention. Although, to me, that's what I think when I see a method called Replace, which is, replace-in-place.
UPDATE
You can also create what you want like this:
public List<Tuple<T, double>> Replace(List<Tuple<T, double>> collection, T term, double value) {
return collection.
Where(x => !x.Item1.Equals(term)).
Append(Tuple.Create(term, value)).
ToList();
}
Using Concat, as mentioned by Oded:
public static class EnumerableEx {
public static IEnumerable<T> Append<T>(this IEnumerable<T> source, T item) {
return source.Concat(new T[] { item });
}
}
One way is to use .Concat(), but you need to have a enumerable rather than a single item as the second argument. To create an array with a single element does work, but is combersome to write.
It is better to write an custom extension method to do so.
One method is to create a new List<T> and add the items from the first list and then the items from the second list. However, it is better to use the yield-keyword instead, so you do not need to create an list and the enumerable will be evaluated in a lazy fashion:
public static class EnumerableExtensions
{
public static IEnumerable<T> Concat<T>(this IEnumerable<T> list, T item)
{
foreach (var element in list)
{
yield return element;
}
yield return item;
}
}
The closest answer I could find came from this post and is:
return collection.Where(x=>!x.Item1.Equals(term)).Concat(new[]{Tuple.Create(term,value)});
Related
I am trying to put together an extension method that will allow me to check if all values of a sequence are the same, and if so return that value, or if not return an average of that value if its an int or double or return a concatenation of that value if it is a string.
For example take the following object and a list
public class Item
{
public int Size {get; set;}
public string Name {get; set;}
}
var itemsTheSame = new List<Item>
{
new Item { Size = 1, Name = "Red" },
new Item { Size = 1, Name = "Red" },
new Item { Size = 1, Name = "Red" }
};
I want to be able to do something like this
itemsTheSame.AllEqual(item => item.Size)); // should return 1 as an int
itemsTheSame.AllEqual(item => item.Name)); // should return "Red" as a string
My generics are not very strong, but this is what ive started off with
public static class Extensions
{
public static int AllEqual<TSource>(this IEnumerable<TSource> list, Func<TSource, int> selector)
{
if (!list.Any()) return default(int);
var first = list.First();
return list.Skip(1).All(selector == first) ? default(int) : default(int);
}
}
It's quite obvious to me that the return type needs to be generic aswell, so came up with this (doesent compile)
public static TReturn AllEqual<TSource, TReturn>(this IEnumerable<TSource> list, Func<TSource, TReturn> selector)
{
if (!list.Any()) return default(TReturn);
var first = list.First(selector);
return list.Skip(1).All(selector == first) ? default(TReturn) : default(TReturn);
}
I am completely confused as I need to be able to pass Func<TSource, TReturn> selector to the .First() to be able to get the value itself, but .First needs a Func<TSource, bool>. I also need to be able to pass the same selector to .All as I only want to check the field specified in the selector for equality.
Is this even possible or am i better off having a set of overloaded methods, one that returns an int and the other that returns a string etc ?
Fiddle here
First doesn't accept a selector, it accepts a predicate. To apply a selector to the first item you need to get the first item (using its parameterless overload), and then invoke the selector on that result.
And to compare objects of an unknown type for equality you won't be able to use the ==operator. It is bound at compile time, so it couldn't account for the specific implementation for that type, in addition to the fact that it might not have one. You should use an IEqualityComparer<T> to compare the items for equality (you can use the default comparer if none is provided).
Also note that your implementation iterates the source enumerable up to 3 times. That could easily be a problem if it's actually a query, and not an already materialized collection. You're really better off just iterating the sequence explicitly, rather than using LINQ operations here, since you can't do it with just one LINQ query.
Try to avoid iterating the IEnumerable multiple times. It could be too expensive, you never know.
You need an implementation for each numeric type because of the average feature.
Average is actually the same whether you have all the same values or not. Just use the Average extension method instead. You may want to wrap it to make the syntax the same. (Or consider to give it a different name, like AllEqualOrAverage, because it doesn't do the same.)
The average of an int is usually a double. Decide whether you want to round it, use floor or actually return a double.
You need Select(selector) to get the values from the items. The argument of First is a predicate, as in Where(predicate).
Proposal:
// for doubles (returning average or default if list is empty)
public static double AllEqual<TSource>(this IEnumerable<TSource> list, Func<TSource, double> selector)
{
// use IList<T> interface to quickly check the size of the
// list if this interface is available. Use ToArray otherwise.
var ilist = list as IList<TSource> ?? list.ToArray();
if (ilist.Count == 0) return 0.0;
list.Select(selector).Average();
}
// for ints (returning average or default if list is empty)
public static double AllEqual<TSource>(this IEnumerable<TSource> list, Func<TSource, int> selector)
{
// use IList<T> interface to quickly check the size of the
// list if this interface is available. Use ToArray otherwise.
var ilist = list as IList<TSource> ?? list.ToArray();
if (ilist.Count == 0) return 0.0;
list.Select(selector).Average();
}
// for everything else (returning unique value or default)
public static TReturn AllEqual<TSource, TReturn>(this IEnumerable<TSource> list, Func<TSource, TReturn> selector)
{
// put 2 distinct values into an array
var distinctItems.Select(selector).Distinct().Take(2).ToArray();
if (distinct.Length == 1) return distinct[0];
default(TReturn);
}
Note that this only works if you know the numeric type at compile time. If you need to return the average of a number which is only recognized at runtime (it is an object at compile time), it gets really hard.
I have class with 5 fields.
public class Test
{
public string name;
public string description;
public int int1;
public int int2;
public int int3;
}
In one of my function I have List<Test> list which has 10 items. Here I want SortedList<string,string> for two properties name & description.
I know, I can achieve this using for each but I want to know How can I do this using LINQ?
Use this:
var result = list.OrderBy(x => x.Name).ThenBy(x => x.Description);
Important:
Don't use multiple calls to OrderBy as they overwrite each other.
The sorted result will be in result. The original list remains unchanged.
The answer from #HugoRune is quite exhaustive, but because you said you want to use Linq, I'd suggest to add an extension method in your scope to help you with your goal:
static class SortedListExtensions
{
public static SortedList<K, V> ToSortedList<K, V, T>(
this IEnumerable<T> source,
Func<T, K> keySelector, Func<T, V> valueSelector)
{
return new SortedList<K,V>(
source.ToDictionary(
cur => keySelector(cur),
cur => valueSelector(cur)));
}
}
this way your SortedList creation is composable in Linq computations:
var sl = list.ToSortedList(f => f.name, f => f.description);
A C# SortedList is a type of dictionary, not actually a list.
If you indeed want a SortedList containing the names as keys and the descriptions as values, you can use this:
SortedList slist = new SortedList(list.ToDictionary(t=>t.name, t=>t.description))
Be aware that if a name occurs twice this will throw an exception, since dictionary keys have to be unique.
For most practical purposes however, the solution posted by Daniel Hilgarth is what I would use, unless you have a library function that specifically requires a SortedList as parameter.
When given an d you could be dealing with a fixed sequence like a list or array, an AST that will enumerate some external datasource, or even an AST on some existing collection. Is there a way to safely "materialize" the enumerable so that enumeration operations like foreach, count, etc. don't execute the AST each time?
I've often used .ToArray() to create this represenation but if the underlying storage is already a list or other fixed sequence, that seems like wasted copying. It would be nice if i could do
var enumerable = someEnumerable.Materialize();
if(enumberable.Any() {
foreach(var item in enumerable) {
...
}
} else {
...
}
Without having to worry that .Any() and foreach try to enumerate the sequence twice and without it unccessarily copying the enumerable.
Easy enough:
public static IList<TSource> Materialize<TSource>(this IEnumerable<TSource> source)
{
if (source is IList<TSource>)
{
// Already a list, use it as is
return (IList<TSource>)source;
}
else
{
// Not a list, materialize it to a list
return source.ToList();
}
}
Original answer:
Same as Thomas's answer, just a bit better according to me:
public static ICollection<T> Materialize<T>(this IEnumerable<T> source)
{
// Null check...
return source as ICollection<T> ?? source.ToList();
}
Please note that this tend to return the existing collection itself if its a valid collection type, or produces a new collection otherwise. While the two are subtly different, I don't think it could be an issue.
Edit:
Today this is a better solution:
public static IReadOnlyCollection<T> Materialize<T>(this IEnumerable<T> source)
{
// Null check...
switch (source)
{
case ICollection<T> collection:
return new ReadOnlyCollectionAdapter<T>(collection);
case IReadOnlyCollection<T> readOnlyCollection:
return readOnlyCollection;
default:
return source.ToList();
}
}
public class ReadOnlyCollectionAdapter<T> : IReadOnlyCollection<T>
{
readonly ICollection<T> m_source;
public ReadOnlyCollectionAdapter(ICollection<T> source) => m_source = source;
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public int Count => m_source.Count;
public IEnumerator<T> GetEnumerator() => m_source.GetEnumerator();
}
Check out this blog post I wrote a couple of years ago: http://www.fallingcanbedeadly.com/posts/crazy-extention-methods-tolazylist
In it, I define a method called ToLazyList that effectively does what you're looking for.
As written, it will eventually make a full copy of the input sequence, although you could tweak it so that instances of IList don't get wrapped in a LazyList, which would prevent this from happening (this action, however, would carry with it the assumption that any IList you get is already effectively memoized).
I have a class which has two HashSet<String> collections as private members. Other classes in my code would like to be able to iterate over those HashSets and read their contents. I don't want to write a standard getter because another class could still do something like myClass.getHashSet().Clear(); Is there any other way to expose the elements of my HashSets to iteration without exposing the reference to the HashSet itself? I'd love to be able to do this in a way that is compatible with for-each loops.
Assuming you're using .NET 3.5, one alternative to writing the yield code yourself is to call a LINQ method. For example:
public IEnumerable<string> HashSet
{
get { return privateMember.Select(x => x); }
}
or
public IEnumerable<string> HashSet
{
get { return privateMember.Skip(0); }
}
There are various LINQ operators which could be used like this - using Skip(0) is probably the most efficient, as after the initial "skip 0 values" loop, it's probably just the foreach/yield return loop shown in the other answers. The Select version will call the no-op projection delegate for each item yielded. The chances of this difference being significant are astronomically small, however - I suggest you go with whatever makes the code clearest to you.
Expose a IEnumerable<T> property:
public IEnumerable<whatevertype> MyHashSet {
get {
return this.myHashSet;
}
}
Of course, the user of this code can cast that IEnumerable<T> to a HashSet<T> and edit elements, so to be on the safe side (while hurting performance), you can do:
public IEnumerable<whatevertype> MyHashSet {
get {
return this.myHashSet.ToArray();
}
}
or:
public IEnumerable<whatevertype> MyHashSet {
get {
foreach(var item in this.myHashSet) {
yield return item;
}
}
}
A more performant method of protection, but less convenient to the caller, is to return an IEnumerator<T>:
public IEnumerator<whatevertype> GetMyHashSetEnumerator() {
return this.myHashSet.GetEnumerator();
}
Add a method/property like this to avoid exposing the actual container:
public IEnumerable EnumerateFirst()
{
foreach( var item in hashSet )
yield return item;
}
You can also use the Select method to create a wrapper than can't be cast back to HashSet<T>:
public IEnumerable<int> Values
{
get { return _values.Select(value => value);
}
This avoids iterating over _values twice, as you would with .ToArray(), but keeps the implementation to a single clean line.
You may also provide a sequence like this:
public IEnumerable<string> GetHashSetOneValues()
{
foreach (string value in hashSetOne)
yield return value;
}
This method may then be called within a foreach loop:
foreach (string value in myObject.GetHashSetOneValues())
DoSomething(value);
This might be quite a bit too late to the party but the easiest way today would be to use Linq. Instead of writing
public IEnumerable<string> GetValues()
{
foreach(var elem in list)
yield return elem;
}
you can write
public IEnumerable<string> GetValues() => list;
Make your getter expose the HashSet as IEnumerable.
private HashSet<string> _mine;
public IEnumerable<string> Yours
{
get { return _mine; }
}
If the generic type is mutable, then that can still be modified, but no items can be added or removed from your HashSet.
A coworker asked me today how to add a range to a collection. He has a class that inherits from Collection<T>. There's a get-only property of that type that already contains some items. He wants to add the items in another collection to the property collection. How can he do so in a C#3-friendly fashion? (Note the constraint about the get-only property, which prevents solutions like doing Union and reassigning.)
Sure, a foreach with Property. Add will work. But a List<T>-style AddRange would be far more elegant.
It's easy enough to write an extension method:
public static class CollectionHelpers
{
public static void AddRange<T>(this ICollection<T> destination,
IEnumerable<T> source)
{
foreach (T item in source)
{
destination.Add(item);
}
}
}
But I have the feeling I'm reinventing the wheel. I didn't find anything similar in System.Linq or morelinq.
Bad design? Just Call Add? Missing the obvious?
No, this seems perfectly reasonable. There is a List<T>.AddRange() method that basically does just this, but requires your collection to be a concrete List<T>.
Try casting to List in the extension method before running the loop. That way you can take advantage of the performance of List.AddRange.
public static void AddRange<T>(this ICollection<T> destination,
IEnumerable<T> source)
{
List<T> list = destination as List<T>;
if (list != null)
{
list.AddRange(source);
}
else
{
foreach (T item in source)
{
destination.Add(item);
}
}
}
Since .NET4.5 if you want one-liner you can use System.Collections.Generic ForEach.
source.ForEach(o => destination.Add(o));
or even shorter as
source.ForEach(destination.Add);
Performance-wise it's the same as for each loop (syntactic sugar).
Also don't try assigning it like
var x = source.ForEach(destination.Add)
cause ForEach is void.
Edit: Copied from comments, Lippert's opinion on ForEach.
Remember that each Add will check the capacity of the collection and resize it whenever necessary (slower). With AddRange, the collection will be set the capacity and then added the items (faster). This extension method will be extremely slow, but will work.
Here is a bit more advanced/production-ready version:
public static class CollectionExtensions
{
public static TCol AddRange<TCol, TItem>(this TCol destination, IEnumerable<TItem> source)
where TCol : ICollection<TItem>
{
if(destination == null) throw new ArgumentNullException(nameof(destination));
if(source == null) throw new ArgumentNullException(nameof(source));
// don't cast to IList to prevent recursion
if (destination is List<TItem> list)
{
list.AddRange(source);
return destination;
}
foreach (var item in source)
{
destination.Add(item);
}
return destination;
}
}
The C5 Generic Collections Library classes all support the AddRange method. C5 has a much more robust interface that actually exposes all of the features of its underlying implementations and is interface-compatible with the System.Collections.Generic ICollection and IList interfaces, meaning that C5's collections can be easily substituted as the underlying implementation.
You could add your IEnumerable range to a list then set the ICollection = to the list.
IEnumerable<T> source;
List<item> list = new List<item>();
list.AddRange(source);
ICollection<item> destination = list;
Or you can just make an ICollection extension like this:
public static ICollection<T> AddRange<T>(this ICollection<T> #this, IEnumerable<T> items)
{
foreach(var item in items)
{
#this.Add(item);
}
return #this;
}
Using it would be just like using it on a list:
collectionA.AddRange(IEnumerable<object> items);
Agree with some guys above and Lipert's opinion.
In my case, it's quite often to do like this:
ICollection<int> A;
var B = new List<int> {1,2,3,4,5};
B.ForEach(A.Add);
To have an extension method for such operation a bit redundant in my view.