2

I have the following list of integers that I need to extract varying lists of integers containing numbers from say 2-4 numbers in count. The code below will extract lists with only 2 numbers.

var numList = new List<int> { 5, 20, 1, 7, 19, 3, 15, 60, 3, 21, 57, 9 };

var selectedNums = (from n1 in numList
                    from n2 in numList
                    where (n1 > 10) && (n2 > 10)
                    select new { n1, n2 }).ToList(); 

Is there any way to build up this Linq expression dynamically so that if I wanted lists of 3 numbers it would be compiled as below, this would save me having to package the similar expression inside a different method.

var selectedNums = (from n1 in numList
                    from n2 in numList
                    from n3 in numList
                    where (n1 > 10) && (n2 > 10) && (n3 > 10)
                    select new { n1, n2, n3 }).ToList();
Alberto
  • 15,626
  • 9
  • 43
  • 56
chillydk147
  • 385
  • 1
  • 10
  • 1
    See: [Computing a Cartesian Product with LINQ](http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx) – Ani May 28 '14 at 08:26
  • I've read this article but can't see how it would help my case – chillydk147 May 28 '14 at 08:37
  • The bit beginning with "But what do you do if you do not know how many sequences there are at compile time?" is *exactly* what you need. You can always repeat the same sequence with `Enumerable.Repeat`. – Ani May 28 '14 at 08:44
  • If LINQ is not an actual requirement then consider the class given in [this blog](http://www.felicepollano.com/2011/08/12/HelperClassForTheCartesianProductOfMoreThanTwoArrays.aspx). – Dialecticus May 28 '14 at 09:04

3 Answers3

1

As with all good questions the way to solve this is with Linq and with Recursion!

    public IEnumerable<IEnumerable<T>> Permutation<T>(int count, IEnumerable<T> sequence)
    {
        if(count == 1)
        {
            foreach(var i in sequence)
            {
                yield return new [] { i };
            }
            yield break;
        }


        foreach(var inner in Permutation(count - 1, sequence))
        foreach(var i in sequence)
        {
            yield return inner.Concat(new [] { i });
        }        
    }

var foo = Permutation<int>(3, numList.Where(x => x > 10));
Aron
  • 15,464
  • 3
  • 31
  • 64
  • I've tried this but there seems to be errors with the syntax, "Permuation" is spelled differently and some closing ")" are missing, can't get it to build – chillydk147 May 28 '14 at 09:41
  • This code is a good candidate for [mega-list of useful helper extensions](http://stackoverflow.com/q/271398/395718). – Dialecticus May 29 '14 at 09:19
  • Just googled this a bit, and I believe the proper name of the method should be something like `CartesianPower` instead of `Permutation`. – Dialecticus May 29 '14 at 10:12
0

[Edited]

You can combine joins and where clauses with loops and conditions, like that:

var q = numList.Where(x => x > 10).Select(x => new List<int>{ x });

for(i = 1; i <= numToTake; ++i)
  q = q.Join(numList.Where(x => x > 10), x=> 0, y => 0, (x, y) => { x.Add(y); return x; });

(that returns a List instead of an anonymous object)

Edit 2: Thanks Aron for the comment.

Joanvo
  • 5,677
  • 2
  • 25
  • 35
  • Sorry guys, but I'm new to Linq and figuring out how either of these 2 suggestions will work for me is way over my head at the moment, I don't need to join 2 lists, I need to create extracted lists of integers with varying count values, as you can see from the above the first expression will have integers list with 2 numbers each, the second expression will have 3 numbers each – chillydk147 May 28 '14 at 09:00
  • When I try to run this I get this error on build - Error 1 The type arguments for method 'System.Linq.Enumerable.Join(System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable, System.Func, System.Func, System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly. – chillydk147 May 28 '14 at 09:21
  • Its because `List.Add` is `void` return. You can "fix" it with `(x, y) => { x.Add(y); return x; }`. But I would advise against using Linq in this way, since functional programming with mutables is just a bit insane. Besides, with permutations, you would very quickly use up a lot of memory, my solution is Lazy evaluated, and can be much more memory efficient. – Aron May 28 '14 at 09:23
  • Ok, thanks anyway, I think I'll stick to using multiple expressions to be on the safe side. – chillydk147 May 28 '14 at 09:28
0

I am not sure if it can help you but an idea how I achieved the same dynamic result based on condition.

var q=(from n in context.TableName
       .......);

if(x!=null) //this can be the condition (you can have yours)
var final=q.Select(i=>i....); //I am using q again here

I am not sure what will be the performance.

Incredible
  • 3,495
  • 8
  • 49
  • 77