1

I have a list of integer:

List<int> foo = new List<int>() {1, 2, 3, 4, 5, 6, 7, 8};

what i need to slice it based on the parity of the index so:

List<int> bar1 = foo.Where((i, v) => v % 2 == 0).ToList();
List<int> bar2 = foo.Where((i, v) => v % 2 == 1).ToList();

The issue is that if now i set

bar1[0] = -1

the original foo list remains unchanged. Is there any work-around in order to change the value of bar1 and bar2 while also changing the values of the original foo list?

Mong Zhu
  • 23,309
  • 10
  • 44
  • 76
  • Do `bar1` has to be a list? You can have an indexer, what will re-run linq query every time and allow you to get/set value of original list (or even its new instance when it changes). – Sinatr Sep 21 '20 at 12:46
  • This is the default behavior. Don't call `ToList` if you want a live view because `ToList` creates a shallow snapshot. – Aluan Haddad Sep 21 '20 at 12:48
  • What operations on `bar1` you want to be reflected to `foo`? Modifications only, or also additions and deletions? – Theodor Zoulias Sep 21 '20 at 12:50
  • Yes i need it to be a list. I know that ToList create a copy that was just an example to better explain my question – simone dozio Sep 21 '20 at 12:50
  • @AluanHaddad, just removing `ToList()` won't help, since `int` is a value type, assiging to it new value will assign it to a copy. – Sinatr Sep 21 '20 at 12:50
  • @TheodorZoulias i need to do addition and multiplication, no deletion. – simone dozio Sep 21 '20 at 12:51
  • 1
    Is `int` your real type ? – Franck Sep 21 '20 at 12:52
  • 2
    So if you call `bar1.Add(13)` you expect the value `13` to be also added to `foo`? – Theodor Zoulias Sep 21 '20 at 12:52
  • 1
    in reality are you working with ints ? or did you used `int` only for this example and in your real code you actually have real objects that are reference types? – Mong Zhu Sep 21 '20 at 12:55
  • @Sinatr it's not a question of value types vs reference types. If the query is over a list that changes, evaluating the query will reflect that. If he wants the opposite, that won't work either way. – Aluan Haddad Sep 21 '20 at 12:55
  • @Franck yes int is my real type – simone dozio Sep 21 '20 at 13:18

2 Answers2

2

To keep the format you want and i do not recommend it you have to fill the collection with reference type object for example this would work

public class ReferenceInt
{
    public int Value { get; set; } = 0;
}

now this would work :

List<ReferenceInt> foo = new List<ReferenceInt>()
{
    new ReferenceInt(){ Value = 1 },
    new ReferenceInt(){ Value = 2 },
    new ReferenceInt(){ Value = 3 },
    new ReferenceInt(){ Value = 4 },
    new ReferenceInt(){ Value = 5 },
    new ReferenceInt(){ Value = 6 },
    new ReferenceInt(){ Value = 7 },
    new ReferenceInt(){ Value = 8 }
};

now you have to filter per value inside the reference object

List<ReferenceInt> bar1 = foo.Where(v => v.Value % 2 == 0).ToList();
List<ReferenceInt> bar2 = foo.Where(v => v.Value % 2 == 1).ToList();

and this changed the value in all collections that has the ReferenceInt in it. So both foo and bar1 will show the same thing

bar1[1].Value = 17;
Franck
  • 4,438
  • 1
  • 28
  • 55
2

You can create a proxy to work in-between of source list and filtered view.

public class Indexer<T> : IEnumerable<T>
{
    public T this[int index]
    {
        get => All().ElementAt(index);
        set
        {
            var i = 0;
            foreach (var item in _source)
                if (_filter(item))
                {
                    if (i++ == index)
                        _source[i] = value;
                    break;
                }
        }
    }

    public IEnumerable<T> All() => _source.Where(o => _filter(o));

    readonly IList<T> _source;
    readonly Func<T, bool> _filter;

    public Indexer(IList<T> source, Func<T, bool> filter)
    {
        _source = source;
        _filter = filter;
    }

    IEnumerator<T> IEnumerable<T>.GetEnumerator() => All().GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => All().GetEnumerator();
}

This proxy will take care to handle indexes and simplify the usage:

List<int> foo = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8 };
var bar1 = new Indexer<int>(foo, o => o % 2 == 0);
Console.WriteLine(string.Join(",", bar1)); // 2,4,6,8
bar1[0] = -1;
Console.WriteLine(string.Join(",", bar1)); // 4,6,8
Console.WriteLine(string.Join(",", foo));  // 1,-1,3,4,5,6,7,8
Sinatr
  • 20,892
  • 15
  • 90
  • 319