简体   繁体   中英

c# : avoid branching while iterating on a dictionary

I have the below code in which i branch for each sample in a dictionary, is there a way either by using LINQ or any other method in which i can avoid branching -> may be a functional approach

Dictionary<string, int> samples = new Dictionary<string, int>()
{
    {"a", 1},
    {"aa", 2},
    {"b", 1},
    {"bb", 3}
};

foreach (var sample in samples)
{
    if (sample.Value ==)
    {
        Console.WriteLine("sample passed");
    }
    else if (sample.Value == 2)
    {
        Console.WriteLine("sample isolated");
    }
    else if (sample.Value == 3)
    {
        Console.WriteLine("sample biased");
    }
}

UPD

What if i have other type of comprasion:

foreach (var sample in samples)
{
    if (sample.Value <= 1)
    {
        Console.WriteLine("sample passed");
    }
    else if (sample.Value <= 2)
    {
        Console.WriteLine("sample isolated");
    }
    else if (sample.Value <= 3)
    {
        Console.WriteLine("sample biased");
    }
}

One option would be to create a list of Action s that you wish to perform, then execute them based on the index. This way your methods can be quite varied. If you need to perform very similar actions for each option, then storing a list of values would be a better than storing Actions.

List<Action> functions = new List<Action>();
functions.Add(() => Console.WriteLine("sample passed"));
functions.Add(() => Console.WriteLine("sample isolated"));
functions.Add(() => Console.WriteLine("sample biased"));

foreach (var sample in samples)
{
  Action actionToExecute = functions[sample.Value - 1];
  actionToExectute();
}

If you wanted to use a dictionary as your comment implies:

Dictionary<int, Action> functions = new Dictionary<int, Action>();
functions.Add(1, () => Console.WriteLine("sample passed"));
functions.Add(2, () => Console.WriteLine("sample isolated"));
functions.Add(3, () => Console.WriteLine("sample biased"));

foreach (var sample in samples)
{
  Action actionToExecute = functions[sample.Value];
  actionToExectute();
}

For this concrete case you can introduce another map( Dictionary or an array, as I did):

Dictionary<string, int> samples = new Dictionary<string, int>()
            {
                {"a", 1},
                {"aa", 2},
                {"b", 1},
                {"bb", 3}
            };

var map = new []
{
    "sample passed",
    "sample isolated",
    "sample biased"
};
foreach (var sample in samples)
{
    Console.WriteLine(map[sample.Value - 1]);
}

As for actual code it highly depends on usecases and how you want to handle faulty situations.

UPD

It seems that if you will be using dictionary for your map there still will be some branching , but if you will not have misses branch prediction should take care of it.

So you have a Dictionary<string, int> . Every item in the dictionary is a KeyValuePair<string, int> . I assume that the string is the name of the sample (identifier), and the int is a number that says something about the sample:

  • if the number equals 0 or 1, the sample is qualified as Passed;
  • if the number equals 2, then you call it Isolated
  • if the number equals 3, then you call it Biased.

All higher numbers are not interesting for you.

You want to group the samples in Passed / Isolated / Biased samples.

Whenever you have a sequence of similar items and you want to make groups of items, where every element has something in common with the other elements in the group, consider using one of the overloads of Enumerable.GroupBy

Let's first define an enum to hold your qualifications, and a method that converts the integer value of the sample into the enum:

enum SampleQualification
{
    Passed,
    Isolated,
    Biased,
}

SampleQualification FromNumber(int number)
{
    switch (number)
    {
        case 2:
            return SampleQualification.Isolated;
        case 3:
            return SampleQualification.Biased;
        default:
            return SampleQualification.Passed;
    }
}

Ok, so you have your dictionary of samples, where every key is a name of the sample and the value is a number that can be converted to a SampleQualification.

Dictionary<string, int> samples = ...
var qualifiedSamples = samples // implements IEnumerable<KeyValuePair<string, int>>

    // keep only samples with Value 0..3
    .Where(sample => 0 <= sample.Value && sample.Value <= 3)

    // Decide where the sample is Passed / Isolated / Biased
    .Select(sample => new
    {
        Qualification = FromNumber(sample.Value)
        Name = sample.Key,           // the name of the sample
        Number = sample.Value,

    })

    // Make groups of Samples with same Qualification:
    .GroupBy(
        // KeySelector: make groups with same qualification:
        sample => sample.Qualification,

        // ResultSelector: take the qualification, and all samples with this qualification
        // to make one new:
        (qualification, samplesWithThisQualification) => new
        {
            Qualification = qualification,
            Samples = samplesWithThisQualification.Select(sample => new
            {
                Name = sample.Name,
                Number = sample.Number,
            })
            .ToList(),
         });

The result is a sequence of items. Where every item has a property Qualification, which holds Passed / Isolated / Biased. Every item also has a list of samples that have this qualification.

// Process Result
foreach (var qualifiedSample in qualifiedSamples)
{
    Console.WriteLine("All samples with qualification " + qualifiedSample.Qualification);
    foreach (var sample in qualifiedSample.Samples)
    {
        Console.WriteLine({0} - {1}, sample.Name, sample.Value);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM