Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine two types of C# lists into one?

I have created two lists say X & Y. These two lists are of different types. (ie List<class_A> X & List<class_B> Y).

Values in both these lists are different. But there is a DateTime field in both these lists.

I need to sort these lists according to the date field.

I have separate functions to print the details of list A and List B.

Suppose the sorted list looks like

  • tuple from list A,
  • tuple from list B,
  • tuple from list A,
  • tuple from list B,

My purpose is to loop through this list and call the appropriate function to display the details. ie if the tuple is from list A then call the function for printing list A's details and vice versa.

like image 995
Shafeek Avatar asked Aug 17 '16 12:08

Shafeek


2 Answers

You can create an interface to host your common properties and functions, then join those lists after casting to this interface and sort it then:

interface ICommon
{
    DateTime DT { get; }
    void Print();
}
class ClassA : ICommon
{
    public DateTime DT { get; set; }
    public void Print() { Console.WriteLine("A " + DT); }
}
class ClassB : ICommon
{
    public DateTime DT { get; set; }
    public void Print() { Console.WriteLine("B " + DT); }
}

public static void Main()
{
    var listA = new List<ClassA> { new ClassA() { DT = DateTime.Now.AddDays(-1) }, new ClassA() { DT = DateTime.Now.AddDays(-3) }};
    var listB = new List<ClassB> { new ClassB() { DT = DateTime.Now.AddDays(-2) }, new ClassB() { DT = DateTime.Now.AddDays(-4) }};

    var orderedResult = listA.Cast<ICommon>()
                             .Concat(listB.Cast<ICommon>())
                             .OrderBy(q => q.DT);
    //or possibly even better:
    //var orderedResult = listA.Concat<ICommon>(listB).OrderBy(q => q.DT);
    foreach (var obj in orderedResult)
        obj.Print();
}
like image 59
slawekwin Avatar answered Oct 04 '22 10:10

slawekwin


If you can change the definition of class A and class B then you should use one of the other answers here which tells you to add an interface to both those classes.

However, if you cannot change the definition of class A and class B (perhaps because they are defined in a library that you can't change) then you must take a different approach.

One solution which keeps things fairly neat is to introduce a wrapper class which holds either an instance of class A or class B (but not both).

Assume that that your classes look like this:

class A
{
    public DateTime Date;
    public double Value;
    public void Print() {}
}

class B
{
    public DateTime Date;
    public string Value;
    public void Print() { }
}

You can write a simple wrapper that looks like this:

class Wrapper
{
    readonly A a;
    readonly B b;

    public Wrapper(A a)
    {
        this.a = a;
    }

    public Wrapper(B b)
    {
        this.b = b;
    }

    public DateTime Date => a?.Date ?? b.Date;

    public void Print()
    {
        if (a != null)
            a.Print();
        else
            b.Print();
    }
}

Then given two lists:

var listA = new List<A>();
var listB = new List<B>();

You can create a sorted list of Wrapper objects:

var sorted = 
    listA.Select(a => new Wrapper(a))
    .Concat(listB.Select(b => new Wrapper(b)))
    .OrderBy(item => item.Date);

Then to call the appropriate Print() method for each item in the sorted list:

foreach (var item in sorted)
    item.Print();
like image 42
Matthew Watson Avatar answered Oct 04 '22 10:10

Matthew Watson