Can not deal with IEnumerable<int?> in generic - c#

I have a method to concatenate strings provided by int?.
public string ConcatenateNumber(IEnumerable<int?> myList)
{
return myList
.Distinct()
.Aggregate(
new StringBuilder(),
(current, next) => current.Append("'").Append(next))
.ToString()
.Substring(1);
}
Now I want to do unit test.
[TestMethod]
public void InputIntegers_Should_Be_Concatenated_When_Consider_Distinct()
{
var myList = CreateEnumerable(1, 2, 2, 3);
var logic = new MainLogic();
var result = logic.ConcatenateNumber(myList);
Assert.AreEqual("1'2'3", result);
}
public IEnumerable<T> CreateEnumerable<T>(params T[] items)
{
if (items == null)
yield break;
foreach (T mitem in items)
yield return mitem;
}
However I have the compile error.
C#: Unknown method ConcatenateNumber(System.Collections.Generic.IEnumerable) of ....
I think it is caused by nullable integer int?. But I am not sure how to fix it.

Explicitly pass the type as a nullable int.
var myList = CreateEnumerable<int?>(1, 2, 2, 3);
For example:
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var p = new Program();
var list = p.CreateEnumerable<int?>(1, 2, 3, 4);
p.DoWork(list);
}
public void DoWork(IEnumerable<int?> enumerable)
{
enumerable.ToList().ForEach(x =>
{
Console.WriteLine(x);
});
}
public IEnumerable<T> CreateEnumerable<T>(params T[] items)
{
if (items == null)
yield break;
foreach (T mitem in items)
yield return mitem;
}
}

public void InputIntegers_Should_Be_Concatenated_When_Consider_Distinct()
{
var myList = CreateEnumerable(1, 2, 2, 3);
var logic = new MainLogic();
var result = logic.ConcatenateNumber(myList);
}
public IEnumerable<T> CreateEnumerable<T>(params T[] items)
{
return items ?? Enumerable.Empty<T>();
}
public class MainLogic
{
public string ConcatenateNumber<T>(IEnumerable<T> myList)
{
// do this if you want to remove nulls
return string.Join("'", myList.Where(i => i != null).Distinct());
//return string.Join("'", myList.Distinct()); // otherwise do this
}
}

Related

Extension Method for List to compute WhereNot

I have one requirement to implement the extension method for List to find out the WhereNot. I am not suppose to use any existing Linq extension method like where etc.
For Example
IEnumerable<int> list = new List<int> {1,2,3,4,5,6};
var whereNotListInt = list.WhereNot((num) => num > 3));
foreach(int i in whereNotListInt)
{
Console.WriteLine(i);
}
Output:-
1
2
3
IEnumerable<string> list = new List<string> {"Cat", "Dog"};
var whereNotListStr = list.WhereNot((str) => str.StartsWith("D")));
foreach(string str in whereNotListStr )
{
Console.WriteLine(str);
}
Output: Cat
I tried below solution, but not able to figure out how to call the function.
public static class Utility
{
public static IEnumerable<T> WhereNot<T>(this IEnumerable<T> list, Func<T, bool> func)
{
foreach (var item in list)
{
yield return func(item);
}
}
}
Since you only want to return items for which the condition is not true, only return each item when func() returns false on that item.
public static class Utility
{
public static IEnumerable<T> WhereNot<T>(this IEnumerable<T> list, Func<T, bool> func)
{
foreach (var item in list)
{
if (!func(item))
yield return item;
}
}
}

How to create a List variable that can contain array of int or string in C#?

How to create a List variable that can contain array of int or string in C#?
I have a class:
public class GoogleAdSlot
{
public IList<int[]> Size { get; set; }
}
How do I create a List such that
return new GoogleAdData
{
AdSlots = new Dictionary<string, GoogleAdSlot>
{
{
"googleAdSquareTile1",
new GoogleAdSlot {
Size = new List<int[]>
{
new[] {250, 250},
new[] {300, 300},
}
}
}
}
};
And:
return new GoogleAdData
{
AdSlots = new Dictionary<string, GoogleAdSlot>
{
{
"googleAdSquareTile1",
new GoogleAdSlot {
Size = new List<int[]>
{
new[] {fluid},
}
}
}
}
};
are both valid.
sample here
List<object> lst = new List<object>() { "12",1,"apple"};
lst.ForEach(m => { Console.WriteLine((m is int) ? "int Varible" : "String Varibale"); });
You can only store one Type and its derived types in a generic List, in the case you described you would have to use List<object> or it's non generic counterpart ArrayList, but non-generic collections are not recommended and don't support newer features like LINQ.
So that would look something like:
var list = new List<object> {"lol", 101};
foreach (var value in list)
{
if(value is string s)
Console.WriteLine(s);
if (value is int i)
Console.WriteLine(i);
}
I'm still not sure exactly what your requirements are, but here's a solution for the most general case.
First
Create a class capable of holding a collection of either integers or strings (but not both). Note that it uses the Collection initialization pattern (twice) (see https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers)
Note that the various GetEnumerator implementations are there only to satisfy the collection initialization pattern. They throw if called, the expectation is that a consumer would call GetIntegerSizes or GetStringSizes.
Here's the code:
public class AdSlotSizes : IEnumerable<int>, IEnumerable<string>
{
public enum CollectionType
{
Uninitialized,
Integers,
Strings,
}
private List<int> _integerSizes;
private List<string> _stringSizes;
public void Add(int sizeToAdd)
{
InitializeList(CollectionType.Integers);
_integerSizes.Add(sizeToAdd);
}
public void Add(string sizeToAdd)
{
InitializeList(CollectionType.Strings);
_stringSizes.Add(sizeToAdd);
}
public CollectionType SizesCollectionType => _collectionType;
private CollectionType _collectionType = CollectionType.Uninitialized;
private void InitializeList(CollectionType forCollectionType)
{
CollectionType oppositeCollectionType = (CollectionType)(((int) CollectionType.Strings + 1) - (int) forCollectionType);
if (_collectionType == oppositeCollectionType)
{
throw new ArgumentException($"A single {nameof(AdSlotSizes)} instance can only hold one type of sizes (int or string)");
}
if (_collectionType != CollectionType.Uninitialized)
{
return;
}
_collectionType = forCollectionType;
if (forCollectionType == CollectionType.Strings)
{
_stringSizes = _stringSizes ?? new List<string>();
}
if (forCollectionType == CollectionType.Integers)
{
_integerSizes = _integerSizes ?? new List<int>();
}
}
public IEnumerable<int> GetIntegerSizes()
{
if (_collectionType != CollectionType.Integers)
{
throw new ArgumentException("Size collection not initialized for integers");
}
foreach (var size in _integerSizes)
{
yield return size;
}
}
public IEnumerable<string> GetStringSizes()
{
if (_collectionType != CollectionType.Strings)
{
throw new ArgumentException("Size collection not initialized for strings");
}
foreach (var size in _stringSizes)
{
yield return size;
}
}
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator<int> IEnumerable<int>.GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
Second
Now I create a class that collections AdSlotSizes instances (again, using the collection initialization pattern). It also implements an indexed property (public AdSlotSizes this[int index]), allowing the consumer to index into the collection:
public class GoogleAddSlot : IEnumerable<AdSlotSizes>
{
private readonly List<AdSlotSizes> _slotSizes = new List<AdSlotSizes>();
public void Add(AdSlotSizes sizes)
{
_slotSizes.Add(sizes);
}
public AdSlotSizes this[int index] => _slotSizes[index];
public IEnumerator<AdSlotSizes> GetEnumerator()
{
foreach (var sizes in _slotSizes)
{
yield return sizes;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator < AdSlotSizes > )this.GetEnumerator();
}
}
Finally, a test method:
public static void TestCollectionSizes()
{
var slot = new GoogleAddSlot
{
{new AdSlotSizes {200, 300, 400} },
{new AdSlotSizes {"fluid", "solid"}},
};
foreach (int size in slot[0].GetIntegerSizes())
{
Debug.WriteLine($"Integer Size: {size}");
}
foreach (string size in slot[1].GetStringSizes())
{
Debug.WriteLine($"String Size: {size}");
}
}
When run, this test method outputs:
Integer Size: 200
Integer Size: 300
Integer Size: 400
String Size: fluid
String Size: solid

What do I need to do to modify this to include indentation?

I recently saw this code by #edplunkett and thought this would be useful in something I'm doing however I'd like to modify to include indentation:
static void Main()
{
var randomCrap = new List<Object>
{
1, "two",
new List<object> { 3, 4 },
5, 6,
new List<object> {
new List<object> { 7, 8, "nine" },
},
};
randomCrap.PrintAll();
}
public static class Extensions
{
public static void PrintAll(this Object root)
{
foreach (var x in root.SelectAll())
{
Console.WriteLine(x);
}
}
public static IEnumerable<Object> SelectAll(this object o)
{
// Thank you, eocron
if (o is String)
{
yield return o;
}
else if (o is IEnumerable)
{
var e = o as IEnumerable;
foreach (var child in e)
{
foreach (var child2 in child.SelectAll())
yield return child2;
}
}
else
{
yield return o;
}
}
}
How easy is it to add indentation each time an IEnumerable is encountered so that you'd get this:
1
two
3
4
5
6
7
8
nine
Something like a starting indent of 0 which increases by one every IEnumerable encountered?
You can do it pretty easily with tuples:
public static class Extensions
{
public static void PrintAll(this Object root)
{
foreach (var x in root.SelectAll(""))
{
Console.WriteLine(x.Item1 + x.Item2);
}
}
public static IEnumerable<(string, Object)> SelectAll(this object o, string indentation)
{
// Thank you, eocron
if (o is String)
{
yield return (indentation, o);
}
else if (o is IEnumerable)
{
var e = o as IEnumerable;
foreach (var child in e)
{
foreach (var child2 in child.SelectAll(indentation + " "))
yield return (child2.Item1, child2.Item2);
}
}
else
{
yield return (indentation, o);
}
}
}
EDIT: Here's a pre-C# 7 version (untested, but should work):
public static class Extensions
{
public static void PrintAll(this Object root)
{
foreach (var x in root.SelectAll(""))
{
Console.WriteLine(x.Item1 + x.Item2);
}
}
public static IEnumerable<Tuple<string, Object>> SelectAll(this object o, string indentation)
{
// Thank you, eocron
if (o is String)
{
yield return Tuple.Create(indentation, o);
}
else if (o is IEnumerable)
{
var e = o as IEnumerable;
foreach (var child in e)
{
foreach (var child2 in child.SelectAll(indentation + " "))
yield return Tuple.Create(child2.Item1, child2.Item2);
}
}
else
{
yield return Tuple.Create(indentation, o);
}
}
}
That would be quite a large change. Right now, it is passing to the Function, and returning from it, single objects. You would have to change that to pass and return two things: th eobject, and the indent level. It could be done, butit would destroy the elegance of #edplunkett's design.
UPDATE: Actually, with Tuples, it's not that bad after all::
static void Main()
{
var randomCrap = new List<Object>
{
1, "two",
new List<object> { 3, 4 },
5, 6,
new List<object> {
new List<object> { 7, 8, "nine" },
},
};
randomCrap.PrintAll();
}
public static class Extensions
{
public static void PrintAll(this Object root)
{
foreach (var x in root.SelectAll(0))
{
Console.WriteLine("{0}{1}", new string('_', x.indent),x.obj);
}
}
public static IEnumerable<(Object obj,int indent)>
SelectAll(this object o, int indent)
{
// Thank you, eocron
if (o is String)
{
yield return (o,indent);
}
else if (o is IEnumerable)
{
var e = o as IEnumerable;
foreach (var child in e)
{
foreach (var child2 in child.SelectAll(indent+1))
yield return child2;
}
}
else
{
yield return (o, indent);
}
}
}

Convert ValueTuple to IEnumerable

Is there a saner way to do the following:
public static class ValueTupleAdditions {
public static IEnumerable<object> ToEnumerable<A, B>(this ValueTuple<A, B> tuple) {
yield return tuple.Item1;
yield return tuple.Item2;
}
public static IEnumerable<object> ToEnumerable<A, B, C>(this ValueTuple<A, B, C> tuple) {
yield return tuple.Item1;
yield return tuple.Item2;
yield return tuple.Item3;
}
[etc]
}
EDIT: Since people are asking for a use case, here you go.
using Xunit;
namespace Whatever {
public class SomeTestClass {
public static IEnumerable<(string, Expression<Func<string, string>>, string)> RawTestData() {
yield return ("Hello", str => str.Substring(3), "lo");
yield return ("World", str => str.Substring(0, 4), "worl");
}
public static IEnumerable<object[]> StringTestData() {
return RawTestData().Select(vt => new object[] { vt.Item1, vt.Item2, vt.Item3 });
// would prefer to call RawTestData().Select(vt => vt.ToArray()) here, but it doesn't exist.
}
[Theory, MemberData(nameof(StringTestData))]
public void RunStringTest(string input, Expression<Func<string, string>> func, string expectedOutput) {
var output = func.Compile()(input);
Assert.Equal(expectedOutput, output);
}
}
}
One way to do this is via the ITuple interface.
public interface ITuple
{
int Length { get; }
object this[int index] { get; }
}
It is only available in .NET Core 2.0, Mono 5.0 and the next version of .NET Framework (unreleased, following 4.7).
It is not (and will never be) available as an add-on to older frameworks via the ValueTuple package.
This API is designed for usage by the C# compiler for future work on patterns.
A bit of reflection:
namespace ConsoleApp1
{
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
var tuple = (1, 2, 3, 4, 5, 6, 7);
var items = ToEnumerable(tuple);
foreach (var item in items)
{
Console.WriteLine(item);
}
}
private static IEnumerable<object> ToEnumerable(object tuple)
{
if (tuple.GetType().GetInterface("ITupleInternal") != null)
{
foreach (var prop in tuple.GetType()
.GetFields()
.Where(x => x.Name.StartsWith("Item")))
{
yield return prop.GetValue(tuple);
}
}
else
{
throw new ArgumentException("Not a tuple!");
}
}
}
}
One way is to use an extension method based on ITuple, see also answer by Julien Couvreur:
public static IEnumerable<T> ToEnumerable<T>( this ITuple tuple ) {
for ( var n = 0; n < tuple.Length; n++ ) yield return (T)tuple[ n ];
}
sample usage:
var directions = (
right: (cx: 1, cy: 0),
down: (cx: 0, cy: 1),
left: (cx: -1, cy: 0),
up: (cx: 0, cy: -1)
);
foreach ( var direction in directions.ToEnumerable<(int cx, int cy)>() ) {
var (cx, cy) = direction;
TryMovePiece( (x + cx, y + cy) );
}

Contravariance in Generics C#

I don't know who resolve this segment code with variance:
I have an abstract father class:
public abstract class PdfObject
{...}
And two child classes:
public class PdfText : PdfObject
{...}
public class PdfImage : PdfObject
{...}
Now, my wrong or empiric code is the next:
public IList<PdfText> GetTexts()
{
List<PdfText> result = new List<PdfText>();
List<PdfObject> list = GetList();
foreach(var item in list)
{
if(item is PdfText) result.Add(item)
}
return result;
}
public List<PdfObject> GetList()
{...}
Well, i read a lot of this theme, but don't stand how use variance in generics or use a better solution for this issue.
Please, help me and thanks.
This doesn't have much to do with variance, directly. Your problem is here:
public IList<PdfText> GetTexts()
{
List<PdfText> result = new List<PdfText>();
List<PdfObject> list = GetList();
foreach(var item in list)
{
if(item is PdfText) result.Add(item)
}
return result;
}
The static type of the item variable is PdfObject so you cannot add it to result; you need to cast it. For example
if (item is PdfText) result.Add((PdfText)item);
This is inefficient because you check the type twice: once for the is operator and once for the cast. Instead, you're supposed to do this:
public IList<PdfText> GetTexts()
{
List<PdfText> result = new List<PdfText>();
List<PdfObject> list = GetList();
foreach(var item in list)
{
var textItem = item as PdfText
if (textItem != null) result.Add(textItem)
}
return result;
}
Or, you can use linq:
var result = GetList().OfType<PdfText>().ToList();
You could do this...
public IList<PdfText> GetTexts()
{
List<PdfText> result = GetList()
.Where(x => x is PdfText)
.Select(x => (PdfText)x)
.ToList();
return result;
}
Edited: This works, but OfType is better.
You could have a better solution in this situation.
public class ClientOfPdfObject<T> where T: PdfObject
{
public List<T> GetItems()
{
List<PdfObject> list = GetList();
var result = new List<T>();
foreach (var pdfObject in list)
{
if (typeof (T) == pdfObject.GetType())
result.Add((T) pdfObject);
}
return result;
}
//Get PdfObjects somewhere (ex. Db)
private List<PdfObject> GetList()
{
var list = new List<PdfObject>
{
new PdfImage(),
new PdfImage(),
new PdfImage(),
new PdfText(),
new PdfText(),
new PdfText(),
new PdfText()
};
return list;
}
}
static void main()
{
var text = new ClientOfPdfObject<PdfText>();
//contains 4 itmes (PdfText)
var pdfTexts = text.GetItems();
var image = new ClientOfPdfObject<PdfImage>();
//contains 3 items (PdfImage)
var pdfImages = image.GetItems();
}
Tomorrow, when you add more pdf objects (ex. PdfGraph), you don't need to change anything.

Categories

Resources