Home > Blockchain >  Return IEnumerable with Linq based on choice
Return IEnumerable with Linq based on choice

Time:10-25

I am trying to figure out a way to solve this issue with Linq, does someone have any idea on how to do this? Trying to find this particular use case has proven to be quite challenging so I hope this question hasn't been asked before even though I suspect I just couldn't find it.

public class Test
{
    public int a;
    public int b;
}

public Test[] testArray;

public enum Choice { A,B, Both = A|B }

public IEnumerable<int> GetEnumerable(Choice choice)
{
    //need to use Linq methods to return an enumerable based on choice
}

//e.g  testArray = { (1,2) (3,4) (5,6)
//calling GetEnumerable(Choice.A)
// 1,3,5
//calling GetEnumerable(Choice.Both)
// 1,2,3,4,5,6

CodePudding user response:

No Linq is needed, I would maybe use a switch expression (though there is a smattering of Linq in here):

public IEnumerable<int> GetEnumerable(Choice choice)
    => choice switch
    {
        Choice.A => testArray.Select(a => a.Item1),
        Choice.B => testArray.Select(a => a.Item2),
        Choice.Both => testArray.SelectMany(a => new[] { a.Item1, a.Item2 }),
        _ => throw new ArgumentException("Invalid choice")
    };

CodePudding user response:

If you insist on single Linq query, can try SelectMany where you can return a required collection, to be flatten e.g.

public IEnumerable<int> GetEnumerable(Choice choice) => testArray
     .SelectMany(item => choice == Choice.Both ? new int[] {item.A, item.B} :
                         choice == Choice.A ? new int[] {item.A} :
                         choice == Choice.B ? new int[] {item.B} :
                                              new int[] {});

However, I'd rather implement a simple foreach loop without any Linq:

// Since you use bit combinations, let's do it explicit with [Flags] attribute
[Flags]
public enum Choice { 
  None = 0, // let have "None" explicit
  A    = 1,
  B    = 2, 
  Both = A|B 
}

public IEnumerable<int> GetEnumerable(Choice choice) {
  foreach (var item in testArray) {
    if (choice.HasFlag(Choice.A))
      yield return item.A;

    if (choice.HasFlag(Choice.B))
      yield return item.B;
  }
}

CodePudding user response:

Theres an inherit problem with your enum, A|B == B, so I changed Both to be it's own case. This solves the problem with one linq query:

public enum Choice { A, B, Both}
public class Test
{
    public int A;
    public int B;

    public Test(int a, int b)
    {
        A = a;
        B = b;
    }
}

public class Program
{
    public static void Main()
    {
        var tests = new List<Test>()
        {
            new Test(1, 2),
            new Test(3, 4),
            new Test(5, 6)
        };

        Console.WriteLine(string.Join(", ", GetEnumerable(tests, Choice.A)));
        Console.WriteLine(string.Join(", ", GetEnumerable(tests, Choice.B)));
        Console.WriteLine(string.Join(", ", GetEnumerable(tests, Choice.Both)));

        /*
         * Console Output:
         *     1, 3, 5
         *     2, 4, 6
         *     1, 2, 3, 4, 5, 6
         */

    }

    private static IEnumerable<int> GetEnumerable(IEnumerable<Test> data, Choice choice)
        => data.SelectMany(d => choice switch
        {
            Choice.A => new List<int> { d.A },
            Choice.B => new List<int> { d.B },
            Choice.Both => new List<int> { d.A, d.B },
            _ => throw new ArgumentException($"No case exists for Choice enum {choice}")
        });

}
  • Related