jscarle
jscarle

Reputation: 1315

Adding items to an ImmutableList<T> inside an ImmutableDictionary<TKey, TValue>

I can't seem to figure out how add items to an ImmutableList inside of an ImmutableDictionary.

I have the following variable:

ImmutableDictionary<string, ImmutableList<string>> _attributes = ImmutableDictionary<string, ImmutableList<string>>.Empty;

To which I'm trying to add a value inside of the list:

string[] attribute = line.Split(':');
if (!_attributes.ContainsKey(attribute[0]))
    _attributes = _attributes.Add(attribute[0], ImmutableList<string>.Empty);
if (attribute.Length == 2)
    _attributes[attribute[0]] = _attributes[attribute[0]].Add(attribute[1]);

However, I get an error saying that the ImmutableList doesn't have a setter. How do I replace the list in the dictionary without having to rebuilt the entire Dictionary?

Upvotes: 4

Views: 1337

Answers (3)

Theodor Zoulias
Theodor Zoulias

Reputation: 44026

You could use the ImmutableDictionary.TryGetValue method, in order to reduce the dictionary lookups from 3 to 2.

var _attributes = ImmutableDictionary.Create<string, ImmutableList<string>>();

string[] parts = line.Split(':');
if (parts.Length == 2)
{
    string attributeName = parts[0];
    string attributeValue = parts[1];

    if (_attributes.TryGetValue(attributeName, out var list))
    {
        _attributes = _attributes.SetItem(attributeName, list.Add(attributeValue));
    }
    else
    {
        _attributes = _attributes.Add(attributeName, ImmutableList.Create(attributeValue));
    }
}

In case you wanted to update the dictionary with thread-safety as an atomic operation, you could use the ImmutableInterlocked.AddOrUpdate method:

ImmutableInterlocked.AddOrUpdate(ref _attributes, attributeName,
    _ => ImmutableList.Create(attributeValue),
    (_, existing) => existing.Add(attributeValue));

Upvotes: 1

Peter Csala
Peter Csala

Reputation: 22848

The ImmutableCollections provide a bunch of different ways how you can construct them.
The general guidance is first populate them then make them immutable.

Create + AddRange

ImmutableDictionary<string, string> collection1 = ImmutableDictionary
    .Create<string, string>(StringComparer.InvariantCultureIgnoreCase)
    .AddRange(
        new[]
        {
            new KeyValuePair<string, string>("a", "a"),
            new KeyValuePair<string, string>("b", "b"),
        });

We have created an empty collection then created another one with some values.

Create + Builder

ImmutableDictionary<string, string>.Builder builder2 = ImmutableDictionary
    .Create<string, string>(StringComparer.InvariantCultureIgnoreCase)
    .ToBuilder();

builder2.AddRange(
    new[]
    {
        new KeyValuePair<string, string>("a", "a"),
        new KeyValuePair<string, string>("b", "b"),
    });

ImmutableDictionary<string, string> collection2 = builder2.ToImmutable();

We have created an empty collection then converted it to a builder.
We have populated it with values.
Finally we have constructed the immutable collection.

CreateBuilder

ImmutableDictionary<string, string>.Builder builder3 = ImmutableDictionary
    .CreateBuilder<string, string>(StringComparer.InvariantCultureIgnoreCase);

builder3
    .AddRange(
        new[]
        {
            new KeyValuePair<string, string>("a", "a"),
            new KeyValuePair<string, string>("b", "b"),
        });

ImmutableDictionary<string, string> collection3 = builder3.ToImmutable();

This is short form of the previous case (Create + ToBuilder)

CreateRange

ImmutableDictionary<string, string> collection4 = ImmutableDictionary
    .CreateRange(new[]
    {
        new KeyValuePair<string, string>("a", "a"),
        new KeyValuePair<string, string>("b", "b"),
    });

This is short form of the first case (Create + AddRange)

ToImmutableDictionary

ImmutableDictionary<string, string> collection5 = new Dictionary<string, string>
{
    { "a", "a" },
    { "b", "b" }
}.ToImmutableDictionary();

Last but not least here we have used a converter.

Upvotes: 6

jscarle
jscarle

Reputation: 1315

The answer was under my nose. Just like I need to call Add and overwrite the original variable when adding items, I need to call SetItem on the Dictionary. Makes sense!

Upvotes: 1

Related Questions