More Standard Query Operators

The Standard Query Operators are great, but at the same time there are ones I'm missing.

They are not hard to implement oneself, but maybe they are worth including in standard set.

1. By


public static IEnumerable<IEnumerable<T>> By<T>(this IEnumerable<T> source, int count);
 


By operator groups the elements of a sequence by count.

This code would return lists of products 10 products in each:

var productPages = products.By(10);
 


sequence.By(count) should be semantically identical to:

sequence.Select((elem, index) => new {Elem = elem, Index = index / count}).GroupBy(e => e.Index, e => e.Elem).Select(g => g.Group)
 


2. Shuffle

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source);

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random random);

 


Shuffle operator randomly shuffles a sequence.

This code would return 3 randomly chosen product:

var productBanners = products.Shuffle().Take(3);
 


sequence.Shuffle(random) should be semantically identical to:

sequence.Select(elem => new {Elem = elem, Tag = random.NextDouble()}).OrderBy(e => e.Tag).Select(e => e.Elem)
 




Answer this question

More Standard Query Operators

  • HK.Lee

    Skip() and Take() are implementation details.  It is very conceivable (if not already the case) that a particular DB implements paging support directly in its query language, in which case it's probably better (and potentially easier) to specify the page number and page size directly, rather than infer that Skip() followed by Take() are intended to refer to paging.  In fact, Skip(n).Take(m) can only be translated to a page operation iff (n/m) is integer.

    Just got back from the Launch in SF.  Looong day..



  • guysky

     Belsen wrote:

    The Standard Query Operators are great, but at the same time there are ones I'm missing.

    They are not hard to implement oneself, but maybe they are worth including in standard set.

    1. By


    public static IEnumerable<IEnumerable<T>> By<T>(this IEnumerable<T> source, int count);
     


    By operator groups the elements of a sequence by count.

    This code would return lists of products 10 products in each:

    var productPages = products.By(10);
     


    sequence.By(count) should be semantically identical to:

    sequence.Select((elem, index) => new {Elem = elem, Index = index / count}).GroupBy(e => e.Index, e => e.Elem).Select(g => g.Group)
     


    2. Shuffle

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source);

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random random);

     


    Shuffle operator randomly shuffles a sequence.

    This code would return 3 randomly chosen product:

    var productBanners = products.Shuffle().Take(3);
     


    sequence.Shuffle(random) should be semantically identical to:

    sequence.Select(elem => new {Elem = elem, Tag = random.NextDouble()}).OrderBy(e => e.Tag).Select(e => e.Elem)
     



  • Ravimcom

    It would be good to have some form of paging support.
    However, what would this code do:


    var productPages = products.By(10);
    return productPages.ToArray();

     


    Would the ToArray() just return an array of 10 or the complete list How would you get the next 10 items

  • spert

    >By operator groups the elements of a sequence by count.

    Not sure how this would vary from using a having where the index value is divided by N.

    >Shuffle operator randomly shuffles a sequence.

    So the use case for that is...

    Also, I don't think I want the internal store shuffled (consider the primacy of document order in XML) first and then just pop the top 3. If the semantic were (randomly select N indexes and construct a new sequence of values), it'd be better. Chaotic, but better.



  • Ed Pinto - MSFT

    I believe that the Skip and Take extension methods provide this.

    From the Standard Query Operators doc:

    When the object returned by Take is enumerated, it enumerates the source sequence and yields elements until the number of elements given by the count argument have been yielded or the end of the source is reached. If the count argument is less than or equal to zero, the source sequence is not enumerated and no elements are yielded.

    The following example creates a sequence of the most expensive 10 products:
    IEnumerable<Product> MostExpensive10 =
        products.OrderByDescending(p => p.UnitPrice).Take(10);

    When the object returned by Skip is enumerated, it enumerates the source sequence, skipping the number of elements given by the count argument and yielding the rest. If the source sequence contains fewer elements than given by the count argument, nothing is yielded. If the count argument is less an or equal to zero, all elements of the source sequence are yielded.

    The following example creates a sequence of all but the most expensive 10 products:
    IEnumerable<Product> AllButMostExpensive10 =
        products.OrderByDescending(p => p.UnitPrice).Skip(10);


    So a sequence.Skip(10).Take(10) would yield the ten items of the second page if the page size was ten. sequence.Skip(20).Take(10) would yield the third page, etc.


  • Manish_Jain

     Keith Farmer wrote:
    But isn't this is an exercise in not having to calculate the details when we can state our intent


     Keith Farmer wrote:
    At some intermediate point, I think you'd need to group by the pages, and then access the page.

    var productPages = products.PageBy(10);


    There’s a tipping point where too much declarative syntax obfuscates the intent of the API in favor of trivial pursuits in natural language programming.  There’s only so much black box stuff that we should do and expect before black box pursuits have a degenerative effect on sustainability.

    I personally don’t know if a PageBy() extension method is beyond that tipping point, but it seems closer to the tipping point than the use of existing extension methods that seeminigly provide the same capabilities with different syntax.

    If I can achieve PageBy() with the existing Skip() and Take() extension methods, then PageBy() starts to smell like keyword and API bloat.

    Is the PageBy() thing simply an alternate syntax for Skip().Take(), or is it something entirely new


  • dX10

    A standard loop with a counter could this as well.  But isn't this is an exercise in not having to calculate the details when we can state our intent


  • shart44

    >Not sure how this would vary from using a having where the index value is divided by N.

    Well...

    1. It shorter and much more readable than Select->GroupBy->Select, and it's convenient to use.

    2. By operator can be implemented in a lazy load way, whereas GroupBy in general case cannot.


            
    public static IEnumerable<IEnumerable<T>> By<T>(this IEnumerable<T> source, int count)
    {
        List<T> list = new List<T>(count);
        foreach (T elem in source)
        {
            list.Add(elem);
            if (list.Count == count)
            {
                yield return list;
                list = new List<T>(count);
            }
        }    
        if (list.Count > 0)
            yield return list;
    }

     


    >>Shuffle operator randomly shuffles a sequence.
    >So the use case for that is...

    Any, where you need a collection iterated in the random order. Advertising, computer games, testing, load balancing, etc.

    >Also, I don't think I want the internal store shuffled (consider the primacy of
    >document order in XML) first and then just pop the top 3. If the semantic were
    >(randomly select N indexes and construct a new sequence of values), it'd be
    >better. Chaotic, but better.

    That may be my fault the meaning of Shuffle operator wasn't clear. Intended sematics was to iterate a sequence in random order, not to shuffle the source collection.



  • EricEarle

    At some intermediate point, I think you'd need to group by the pages, and then access the page.

    var productPages = products.PageBy(10);

    Console.WriteLine("Page 3: {0} products", productPages[2].ToArray());

    2)
    foreach (Page<Product> page in productPages)
    {
       Console.WriteLine("Page {0}: {1} products, element indices {2} through {3}",
           page.PageNumber, // == page.Index + 1
          page.Count,
          page.MinIndex,
          page.MaxIndex
    }

    3)
    var pageN =
       from p in products
       page size 10 
       page index n // dunno about the syntax...



  • Noam

    By:  I think what you're aiming for is paging support, right   Depending on the characteristics of the underlying data, that may or may not be efficient (eg, in the case where the data can only be scanned, but not skipped).  However, it would be worth having.  It'd make some things much easier.


  • More Standard Query Operators