4

I am getting started with C# and got the following class:

using System;
using System.Collections.Generic;

class PrefixMapSum : Dictionary<String, int> {

    public bool insert(String key, int value) {
        return base.TryAdd(key, value);
    } 

    public int sum(String prefix) {
        int sum = 0;

        foreach (String key in base.Keys) {
            if (key.StartsWith(prefix)) {
                sum = sum + base[key];
            }
        }

        return sum;
    }
}

Now I'd love to shorten the following part of the code with lambda-expressions:

        foreach (String key in base.Keys) {
            if (key.StartsWith(prefix)) {
                sum = sum + base[key];
            }
        }

I tried with:

new List<String>(base.Keys).ForEach(key => key.StartsWith(prefix) ? sum = sum + base[key] : sum = sum);

Yet i am running into this error: CS0201

I am coming from Java and I can't quite figure out why it does not work. Can anyone explain what I should do differently (and why)?

2
  • You'd get a similar error in Java. You tried to use the ternary operator as if it were an if block. Assignments are expressions, so this call will return a value BUT ForEach expects statements that do something, not expressions Commented Feb 4, 2020 at 9:54
  • 1
    There are better ways to filter and aggregate values in Java, using streams for example. The syntax itself is quite similar to LINQ's Commented Feb 4, 2020 at 9:54

5 Answers 5

6

This will only work when there is at least one element after the filtering.

base.Keys
   .Where(key=> key.StartsWith(prefix))
   .Sum(base[key])

If there can be none(The method cannot sum), you can use this DefaultIfEmptyso you will have the sum as 0.

base.Keys
   .Where(key=> key.StartsWith(prefix))
   .Select(key=> base[key])
   .DefaultIfEmpty(0)
   .Sum()

For performance reasons you would probably like to avoid using the indexer, and iterate the dictionary yourself.

var defaultZero = new KeyValuePair<string, int>(string.Empty, 0);
var sum = this
          .Where(pair => pair.Key.StartsWith(prefix))
          .DefaultIfEmpty(defaultZero)
          .Sum(pair => pair.Value);

If you need to handle the case where there is no element separately you can do something like

var elements =this
   .Where(pair => pair.Key.StartsWith(prefix))
   .Select(x=>x.Value);
if(!elements.Any()){
    // your logic here
}else{
    sum= elements.Sum();
    // other stuff
}
Sign up to request clarification or add additional context in comments.

1 Comment

thanks for the explanation, the example above tells me CS0103 (key is not known for the select-part)
4

You can achieve it with System.Linq, use Where method to filter the keys, then get Sum of values for filtered keys by passing Func<string, int> selector

public int sum(string prefix)
{
    return base.Keys.Where(key => key.StartsWith(prefix)).Sum(key => base[key]);
}

Comments

3

You can put it as a simple Linq query:

using System.Linq;

...

int sum = this
  .Where(pair => pair.Key.StartsWith(prefix))
  .Sum(pair => pair.Value);

1 Comment

base.Where didn't work. You should work on this.
0

You can use Linq:

            var sum = base.Keys.Where(key => key.StartsWith(prefix))
                          .Sum(key => base[key]);

Comments

0

For performance, avoid the indexer (ie: calls to base[key]) if you can. Here you can directly work on the KeyValuePair enumeration:

public int Sum(string prefix) => this.Where(kvp => kvp.Key.StartsWith(prefix)).Sum(kvp => kvp.Value);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.