NiTeC57
NiTeC57

Reputation: 31

C# Lists reference with two?

I need 2 lists which are separate and one list containing the items of these two lists.

List<int> list1 = new List<int>();
List<int> list2 = new List<int>();

List<int> list3 = list1 & list2

When I add some integers in list1, I would like them to appear in list3 as well. I want the same behavior when a new item is added into list2.

A reference of more than one lists.

Is this possible?

Upvotes: 3

Views: 119

Answers (3)

Servy
Servy

Reputation: 203811

No. You can create your own custom type that exposes methods similar to what a List<T> does (an indexer, Add, Remove methods, etc.), possibly even implementing IList<T>, but it wouldn't be a List<T>.

public class CompositeList<T> : IList<T>
{
    private IList<IList<T>> lists;
    public CompositeList(IList<IList<T>> lists)
    {
        this.lists = lists;
    }
    public CompositeList(params IList<T>[] lists)
    {
        this.lists = lists;
    }

    public int IndexOf(T item)
    {
        int globalIndex = 0;
        foreach (var list in lists)
        {
            var localIndex = list.IndexOf(item);
            if (localIndex >= 0)
                return globalIndex + localIndex;
            else
                globalIndex += list.Count;
        }
        return -1;
    }

    public void Insert(int index, T item)
    {
        //note that there is an ambiguity over where items should be inserted
        //when they are on the border of a set of lists.
        foreach (var list in lists)
        {
            //use greater than or equal instead of just greater than to have the inserted item 
            //go in the later of the two lists in ambiguous situations, 
            //rather than the former.
            if (index > list.Count)
                index -= list.Count;
            else
            {
                list.Insert(index, item);
                return;
            }
        }
        //TODO deal with having no lists in `lists`
        //TODO deal with having only empty lists in `lists`
        throw new IndexOutOfRangeException();
    }

    public void RemoveAt(int index)
    {
        foreach (var list in lists)
        {
            if (index > lists.Count)
                index -= list.Count;
            else
                list.RemoveAt(index);
        }
        throw new IndexOutOfRangeException();
    }

    public T this[int index]
    {
        get
        {
            foreach (var list in lists)
            {
                if (index > lists.Count)
                    index -= list.Count;
                else
                    return list[index];
            }

            throw new IndexOutOfRangeException();
        }
        set
        {
            foreach (var list in lists)
            {
                if (index > lists.Count)
                    index -= list.Count;
                else
                    list[index] = value;
            }

            throw new IndexOutOfRangeException();
        }
    }

    public void Add(T item)
    {
        if (!lists.Any())
            lists.Add(new List<T>());

        lists[lists.Count - 1].Add(item);
    }

    public void Clear()
    {
        foreach (var list in lists)
            list.Clear();
    }

    public bool Contains(T item)
    {
        return lists.Any(list => list.Contains(item));
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        if (array.Length - arrayIndex - Count < 0)
            throw new ArgumentException("The array is not large enough.");
        int iterations = Math.Min(array.Length, Count);
        for (int i = arrayIndex; i < iterations; i++)
            array[i] = this[i];
    }

    public int Count
    {
        get { return lists.Sum(list => list.Count); }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(T item)
    {
        foreach (var list in lists)
        {
            if (list.Remove(item))
                return true;
        }
        return false;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return lists.SelectMany(x => x).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1500055

No, you can't do that with List<T> directly. However, you could declare:

IEnumerable<int> union = list1.Union(list2);

Now that will be lazily evaluated - every time you iterate over union, it will return every integer which is in either list1 or list2 (or both). It will only return any integer once.

If you want the equivalent but with concatenation, you can use

IEnumerable<int> concatenation = list1.Concat(list2);

Again, that will be lazily evaluated.

As noted in comments, this doesn't expose all the operations that List<T> does, but if you only need to read from the "combined integers" (and do so iteratively rather than in some random access fashion) then it may be all you need.

Upvotes: 8

D Stanley
D Stanley

Reputation: 152521

Is it possible?

Not with List since it's a static data structure - however you could use a query that is the concatenation of the two lists. Then whenever you enumerate the query it will show the current contents:

List<int> list1 = new List<int>();
List<int> list2 = new List<int>();

IEnumerable<int> list3 = list1.Concat(list2);

But as soon as you materialize the query into a data structure (by calling ToList, ToArray, etc.) the contents are static and will not update if one of the underlying lists updates.

Upvotes: 2

Related Questions