C# Arbitrarily Nested List Transformation with Lambda Expression - c#

I am trying to do some transformations on lists of different classes. For example, there are class A and class B.
class A
{
private int Data;
public A(int InputData)
{
this.Data = InputData;
}
public int GetData()
{
return this.Data;
}
public B ToB()
{
return new B(this);
}
}
class B
{
private double Data;
public B(A ObjectA)
{
this.Data = Math.Cos(ObjectA.GetData()); // For example
}
public double GetData()
{
return this.Data;
}
}
Then, ListB1 is as the transformation result from ListA1.
List<A> ListA1 = new List<A>() { new A(0), new A(1), new A(2) };
List<B> ListB1 = new List<B>();
foreach (var EachItem in ListA1)
{
ListB1.Add(EachItem.ToB());
}
I am wondering that is there any way to use lambda expression to perform this example? Maybe something like List<B> ListB1 = ListA1.ConvertAll(ObjectA => { ObjectA.ToB(); }); However, it seems that the input parameter of ConvertAll method is designed as a Converter object, not lambda expression. Moreover, is there any easy way to deal with arbitrarily nested list cases, such as the following ListA2? I know that ListB2 could be generated by nested foreach loop, but I am looking forward to find a way like List<List<B>> ListB2 = ListA2.ConvertAll(ObjectA => { ObjectA.ToB(); });. Then the same structure could be applied to arbitrarily nested list.
List<List<A>> ListA2 = new List<List<A>>();
ListA2.Add(ListA1);
ListA2.Add(ListA1);
List<List<B>> ListB2 = new List<List<B>>();
foreach (var EachItem1 in ListA2)
{
List<B> temp = new List<B>();
foreach (var EachItem2 in EachItem1)
{
temp.Add(EachItem2.ToB());
}
ListB2.Add(temp);
}
Any suggestions are welcome.

You can Do Something like
ListA2.ForEach(x => x.ToB()).ToList();
That way you now have a list of B

You could use Linq's Select to translate each item;
List<B> ListB1 = ListA1.Select(a => a.ToB()).ToList();
List<List<B>> ListB2 = ListA2.Select(a2 => a2.Select(a => a.ToB()).ToList()).ToList();

Related

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.

how to yield an anonymous class in IEnumerable.GroupBy<T> "on the fly" (without enumerating result) in C# LINQ?

I do like this now (thanks to StackOverflow):
IEnumerable<object> Get()
{
var groups = _myDatas.GroupBy(
data => new { Type = data.GetType(), Id = data.ClassId, Value = data.Value },
(key, rows) => new
{
ClassId = key.Id,
TypeOfObject = key.Type,
Value = key.Value,
Count = rows.Count()
}));
foreach (var item in groups)
{
yield return item;
}
}
IEnumerable<MyData> _myDatas;
But is possible to make faster or more "elegant" by not having last foreach loop, but yielding it when the group/anonymous class instance is created?
I would guess fastest way would be to write it open and:
sort the _myDatas
enumerate it and when group changes yield the last group
But I'm trying to learn some LINQ (and C# features in general) so I don't want to do that.
The rest of example is here:
public abstract class MyData
{
public int ClassId;
public string Value;
//...
}
public class MyAction : MyData
{
//...
}
public class MyObservation : MyData
{
//...
}
You should be able to return groups directly, though you might need to change your return type from IEnumerable<Object> to just IEnumerable.
So:
IEnumerable Get()
{
var groups = _myDatas.GroupBy(
// Key selector
data => new {
Type = data.GetType(),
Id = data.ClassId,
Value = data.Value
},
// Element projector
(key, rows) => new
{
ClassId = key.Id,
TypeOfObject = key.Type,
Value = key.Value,
Count = rows.Count()
}
);
return groups;
}
groups has the type IEnumerable< IGrouping< TKey = Anonymous1, TElement = Anonymous2 > >, so you can return it directly.

method returns dynamic type of List

If I have the following:
class Super {
//stuff
}
class Sub1 : Super {
//stuff
}
class Sub2 : Super {
//stuff
}
class Sub3 : Super {
//stuff
}
Run Class
class Run {
//list of type Super with a bunch of sub objects (sub1,sub2,sub3)
List<Super> superList = sub1,2,3 list;
List<Super> partialSuperList;
void doStuff() {
Type subObjectType = superList[0].GetType();
if (subObjectType == typeof(sub1)) {
partialSuperList = categorize(subObjectType);
} else if (subObjectType == typeof(sub2)) {
partialSuperList = categorize(subObjectType);
} else if (subObjectType == typeof(sub3)) {
partialSuperList = categorize(subObjectType);
}
}
List<Super> categorize(Type type) {
List<type> subTypeList = new List<type>();
//loop through subtype list and add it to a super type list
return SuperList;
}
}
as you can see I'm trying to dynamically create a list with a "type" coming from the argument to the method, am I doing this right or is there another way to do this?
It's hard to judge if you are doing the right thing since your pseudo code doesn't compile at the moment.
I think you are trying filter a list based on some type. I think the Linq method OfType<TResult> is what you are looking for.
var superList = new List<Super> { ... };
var sub1List = superList.OfType<Sub1>().ToList();
var sub2List = superList.OfType<Sub2>().ToList();
var sub3List = superList.OfType<Sub3>().ToList();
If you are want to filter on a type you do not know at compile-time, you can do the following:
var superList = new List<Super> { ... };
var someType = superList.First().GetType();
var filteredSuperList = superList.Where(x => x.GetType() == someType).ToList();
Make the method generic:
List<T> doStuff<T>() where T : Super
{
List<T> list = new List<T>();
// use list
}
You don't have the casting problems you have now, and you can check T using is:
if (T is Sub1)
{
... // do something
}

Do a correct union on two List<SelectListItem>

retval.AddRange(oldList.Union(newList));
Both the oldList and newList are declared here
List<SelectListItem> oldList = new List<SelectListItem>();
List<SelectListItem> newList = new List<SelectListItem>();
I want to union these two lists, removing duplicate items that have the same "text" property. If there is a dupe between newList and oldList on the text property, it should keep the text/value pair of the newList.
The above union doesn't seem to be doing anything besides concat'ing the lists almost, don't know why.
What am I doing wrong?
I want to union these two lists, removing duplicate items that have the same "text" property.
The Union method will not handle this requirement. You could do this via:
retVal.AddRange(newList); // Since you want to keep all newList items, do this first
// Add in all oldList items where there isn't a match in new list
retVal.AddRange(oldList.Where(i => !newList.Any(n => i.Text == n.Text)));
From reading the documentation it appears that A.Union(B) in effect just adds to A those items from B that aren't already in A. That is:
When the object returned by this
method is enumerated, Union enumerates
first and second in that order and
yields each element that has not
already been yielded.
A quick test bears that out. So oldList.Union(newList) will keep the old values whereas newList.Union(oldList) will give you the new values.
Here's my test code:
class MyThing
{
public string Text { get; private set; }
public int Version { get; private set; }
public MyThing(string t, int v)
{
Text = t;
Version = v;
}
public override int GetHashCode()
{
return Text.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
MyThing other = obj as MyThing;
if (other == null)
return false;
return this.Text.Equals(other.Text);
}
}
static List<MyThing> oldList = new List<MyThing>()
{
new MyThing("Foo", 0),
new MyThing("Bar", 0),
new MyThing("Fooby", 0),
};
static List<MyThing> newList = new List<MyThing>()
{
new MyThing("Barby", 1),
new MyThing("Bar", 1)
};
static void DoIt()
{
var unionOldNew = oldList.Union(newList);
Console.WriteLine("oldList.Union(newList)");
foreach (var t in unionOldNew)
{
Console.WriteLine("{0}, {1}", t.Text, t.Version);
}
Console.WriteLine();
var unionNewOld = newList.Union(oldList);
Console.WriteLine("newList.Union(oldList)");
foreach (var t in unionNewOld)
{
Console.WriteLine("{0}, {1}", t.Text, t.Version);
}
}

convert a list of objects from one type to another using lambda expression

I have a foreach loop reading a list of objects of one type and producing a list of objects of a different type. I was told that a lambda expression can achieve the same result.
var origList = List<OrigType>(); // assume populated
var targetList = List<TargetType>();
foreach(OrigType a in origList) {
targetList.Add(new TargetType() {SomeValue = a.SomeValue});
}
Try the following
var targetList = origList
.Select(x => new TargetType() { SomeValue = x.SomeValue })
.ToList();
This is using a combination of Lambdas and LINQ to achieve the solution. The Select function is a projection style method which will apply the passed in delegate (or lambda in this case) to every value in the original collection. The result will be returned in a new IEnumerable<TargetType>. The .ToList call is an extension method which will convert this IEnumerable<TargetType> into a List<TargetType>.
If you know you want to convert from List<T1> to List<T2> then List<T>.ConvertAll will be slightly more efficient than Select/ToList because it knows the exact size to start with:
target = orig.ConvertAll(x => new TargetType { SomeValue = x.SomeValue });
In the more general case when you only know about the source as an IEnumerable<T>, using Select/ToList is the way to go. You could also argue that in a world with LINQ, it's more idiomatic to start with... but it's worth at least being aware of the ConvertAll option.
var target = origList.ConvertAll(x => (TargetType)x);
List<target> targetList = new List<target>(originalList.Cast<target>());
I believe something like this should work:
origList.Select(a => new TargetType() { SomeValue = a.SomeValue});
Here's a simple example..
List<char> c = new List<char>() { 'A', 'B', 'C' };
List<string> s = c.Select(x => x.ToString()).ToList();
var list1 = new List<Type1>();
var list2 = new List<Type2>();
list1.ForEach(item => list2.Add(new Type2() { Prop1 = value1 }));
Assume that you have multiple properties you want to convert.
public class OrigType{
public string Prop1A {get;set;}
public string Prop1B {get;set;}
}
public class TargetType{
public string Prop2A {get;set;}
public string Prop2B {get;set;}
}
var list1 = new List<OrigType>();
var list2 = new List<TargetType>();
list1.ConvertAll(x => new OrigType { Prop2A = x.Prop1A, Prop2B = x.Prop1B })
Or with a constructor & linq with Select:
public class TargetType {
public string Prop1 {get;set;}
public string Prop1 {get;set;}
// Constructor
public TargetType(OrigType origType) {
Prop1 = origType.Prop1;
Prop2 = origType.Prop2;
}
}
var origList = new List<OrigType>();
var targetList = origList.Select(s=> new TargetType(s)).ToList();
The Linq line is more soft! ;-)
If you need to use a function to cast:
var list1 = new List<Type1>();
var list2 = new List<Type2>();
list2 = list1.ConvertAll(x => myConvertFuntion(x));
Where my custom function is:
private Type2 myConvertFunction(Type1 obj){
//do something to cast Type1 into Type2
return new Type2();
}
for similar type class.
List<targetlist> targetlst= JsonConvert.DeserializeObject<List<targetlist>>(JsonConvert.SerializeObject(<List<baselist>));
If the types can be directly cast this is the cleanest way to do it:
var target = yourList.ConvertAll(x => (TargetType)x);
If the types can't be directly cast then you can map the properties from the orginal type to the target type.
var target = yourList.ConvertAll(x => new TargetType { SomeValue = x.SomeValue });
If casting when mapping from one list to another is required, from convertall, you can call a function to test the casting.
public int StringToInt(String value)
{
try
{
return Int32.Parse(value);
}
catch (Exception ex)
{
return -1;
}
}
[Fact]
public async Task TestConvertAll()
{
List<String> lstString = new List<String>{"1","2","3","4","5","6","7","8","9","10" };
List<int> lstInt = lstString.ConvertAll(new Converter<String, int>(StringToInt));
foreach (var item in lstInt)
{
output.WriteLine("{0}", item);
}
if (lstInt.Count()>0) {
Assert.True(true);
}
}
We will consider first List type is String and want to convert it to Integer type of List.
List<String> origList = new ArrayList<>(); // assume populated
Add values in the original List.
origList.add("1");
origList.add("2");
origList.add("3");
origList.add("4");
origList.add("8");
Create target List of Integer Type
List<Integer> targetLambdaList = new ArrayList<Integer>();
targetLambdaList=origList.stream().map(Integer::valueOf).collect(Collectors.toList());
Print List values using forEach:
targetLambdaList.forEach(System.out::println);

Categories

Resources