简体   繁体   中英

How to simplify this lambda expression?

I have this class which contains int array

 public class Combination
 {
    public int[] CombinationSet { get; set; }
 }

There is static List with many instances of this class

public static List<Combination> Combinations = new List<Combination>();

Now I need methods to find combinations on that list so far I have

For 2

 public static List<Combination> FindCombinations(int x,int y)
    {
        if (x == y)
        {
            return Combinations.Where(
           lenght => lenght.CombinationSet.Length == 2)
           .Where( 
                data => (data.CombinationSet[0] == x && data.CombinationSet[1] == y)
               || (data.CombinationSet[1] == x && data.CombinationSet[0] == y)
               ).ToList();
        }
        else
        {
            return Combinations.Where(
          lenght => lenght.CombinationSet.Length == 2)
          .Where(data => data.CombinationSet.Contains(x)
          && data.CombinationSet.Contains(y)
          ).ToList();
        }

    }

Example : if list contains sets : { 1 , 2} , { 1, 3} , { 1 , 2}

and you would call FindCombination(1,2), you would get back list with two instances

It is working fine however for 4 parameters it would be over 24 rows in else statement. I need maximum of 4 I just wonder if there is some more clever way of doing this.

for 3 it looks like this

 public static List<Combination> FindCombinations(int x, int y,int z)
    {
        if(x == y || x == z || y == z)
        {
            return Combinations.Where(
            lenght =>
            lenght.CombinationSet.Length == 3).
            Where(
                inner => ( (
                    inner.CombinationSet[0] == x && inner.CombinationSet[1] == y && inner.CombinationSet[2] == z)
                || (inner.CombinationSet[0] == x && inner.CombinationSet[2] == y && inner.CombinationSet[1] == z)
                || (inner.CombinationSet[1] == x && inner.CombinationSet[0] == y && inner.CombinationSet[2] == z)
                || (inner.CombinationSet[1] == x && inner.CombinationSet[2] == y && inner.CombinationSet[0] == z)
                || (inner.CombinationSet[2] == x && inner.CombinationSet[0] == y && inner.CombinationSet[1] == z)
                || (inner.CombinationSet[2] == x && inner.CombinationSet[1] == y && inner.CombinationSet[0] == z)
                )).ToList();
        }
        else
        {
            return Combinations.Where(
            length =>
            length.CombinationSet.Length == 3
            ).Where(data => data.CombinationSet.Contains(x)
            && data.CombinationSet.Contains(y)
            && data.CombinationSet.Contains(z)
            ).ToList();
        }

    }

If you have an object that contains the the list, you may want to put the logic directly on the object. Although really there is no reason (you've provided) to use an array, so might as well make it a List.

public class Combination
{
    public List<int> CombinationSet { get; set; }

    public bool IsSequenceEqual(params int[] values)
    {
        return CombinationSet.SequenceEqual(values);
    }

    public override string ToString()
    {
        return string.Join(", ", CombinationSet);
    }
}

So each Combination doesn't care how many parameters you pass, it just wants to make sure the order and number of items are equal. So extend this to the function:

public static List<Combination> FindCombinations(params int[] values)
{
    var result = _Combinations
        .Where(c => c.IsSequenceEqual(values))
        .ToList();

    return result;
}

And seems to work in a DotNetFiddle example :

public static void Main()
{
    _Combinations.Add(new Combination{ CombinationSet = new List<int>{ 1, 2, 3}});
    _Combinations.Add(new Combination{ CombinationSet = new List<int>{ 2, 3, 4}});
    _Combinations.Add(new Combination{ CombinationSet = new List<int>{ 3, 2, 1}});
    _Combinations.Add(new Combination{ CombinationSet = new List<int>{ 1, 2, 3}});

    Console.WriteLine("Test 1  (1,2,3)");
    foreach(var result in FindCombinations(1,2,3))
    {
        Console.WriteLine(result.ToString());
    }

    Console.WriteLine("Test 1  (3,2,1)");
    foreach(var result in FindCombinations(3,2,1))
    {
        Console.WriteLine(result.ToString());
    }

    Console.WriteLine("Test 1  (1,2)");
    foreach(var result in FindCombinations(1,2))
    {
        Console.WriteLine(result.ToString());
    }
}

resulting in:

Test 1 (1,2,3)

1, 2, 3

1, 2, 3

Test 1 (3,2,1)

3, 2, 1

Test 1 (1,2)

If the order doesn't matter, then just check if all three values are present in the array (don't worry about using an index to check the position of each integer):

return Combinations.Where(c => c.CombinationSet.Contains(x) 
    && c.CombinationSet.Contains(y) 
    && c.CombinationSet.Contains(z));

If it must have exactly the number of elements that were passed in, you could add additional AND statements to match the array length, etc.

EDIT: Based on your comment, what if you ordered the integers before comparing, and then returned the Combination only if the ordered sets are identical. This should work as long as the combination must contain the same number of elements that are passed in:

return Combinations.Where(c => c.CombinationSet.OrderBy(i => i).SequenceEqual(new int[] { x, y, z }.OrderBy(j => j)));

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