I use LINQ to Objects instructions on an ordered array. Which operations shouldn't I do to be sure the order of the array is not changed?
I examined the methods of System.Linq.Enumerable, discarding any that returned non-IEnumerable results. I checked the remarks of each to determine how the order of the result would differ from order of the source.
Preserves Order Absolutely. You can map a source element by index to a result element
- AsEnumerable
- Cast
- Concat
- Select
- ToArray
- ToList
Preserves Order. Elements are filtered or added, but not re-ordered.
- Distinct
- Except
- Intersect
- OfType
- Prepend (new in .net 4.7.1)
- Skip
- SkipWhile
- Take
- TakeWhile
- Where
- Zip (new in .net 4)
Destroys Order - we don't know what order to expect results in.
- ToDictionary
- ToLookup
Redefines Order Explicitly - use these to change the order of the result
- OrderBy
- OrderByDescending
- Reverse
- ThenBy
- ThenByDescending
Redefines Order according to some rules.
- GroupBy - The IGrouping objects are yielded in an order based on the order of the elements in source that produced the first key of each IGrouping. Elements in a grouping are yielded in the order they appear in source.
- GroupJoin - GroupJoin preserves the order of the elements of outer, and for each element of outer, the order of the matching elements from inner.
- Join - preserves the order of the elements of outer, and for each of these elements, the order of the matching elements of inner.
- SelectMany - for each element of source, selector is invoked and a sequence of values is returned.
- Union - When the object returned by this method is enumerated, Union enumerates first and second in that order and yields each element that has not already been yielded.
Edit: I've moved Distinct to Preserving order based on this implementation.
private static IEnumerable<TSource> DistinctIterator<TSource>
(IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
{
Set<TSource> set = new Set<TSource>(comparer);
foreach (TSource element in source)
if (set.Add(element)) yield return element;
}
Distinct
method) just meant to say "unsorted", not "in unpredictable order". I'd say Distinct
belongs to the filtering category above, just like Where
. –
Aegaeon ILookup
: The IGrouping<TKey, TElement> objects are yielded in an order based on the order of the elements in source that produced the first key of each IGrouping<TKey, TElement>. Elements in a grouping are yielded in the order they appear in source., taken from the GroupBy documentation. Since ILookup
has IGrouping
s, it's guaranteed to preserve order, right? –
Ounce GroupBy
. Lookup is the underlying data structure of both GroupBy
and ToLookup
. Therefore ToLookup
preserves ordering just as well as Distinct
does: neither is documented, both works (for ≥ 6 years now). –
Phenothiazine ToLookup
changes the keys order, but the elements preserve the order of the source enumerable. –
Steger ToLookup
which calls Lookup.Create
. It runs serially on the source enumerable and calls Grouping.Add
that encapsulate a simple array buffer. –
Steger Lookup.Create
calls lookup.GetGrouping() for every item of the source, which creates groups in a manner that preserves the ordering of keys, as I detailed above. –
Phenothiazine Aggregate
. Aggregate
follows the order because the concept of Aggregate
requires that it follows the order. Here's the Reference Source to verify this. I realize that since it's kind of a given, it might not be important enough to mention, but it's not mentioned anywhere on this Q&A. –
Mobocracy Skip
(and other things on the order preserved list) return an IOrderedEnumerable
? –
Wawro IOrderedEnumerable<T>
is for. Without this interface, we could not call ThenBy
. Does it make sense to call customers.Skip(5).ThenBy(c => c.Name)
? Skip
does not define an ordering - its implementation doesn't change the order. –
Strasbourg IEnumerable
doesn't guarantee order, and I wouldn't've assumed I could count on something returning IEnumerable
"doesn't change the order". It seems smelly to say that a function that returns an unordered type guarantees that unordered type's order is preserved. Wait, what? ;^) If customers
has an internalized idea of how it's ordered (ie, the formula by which it's ordered, which I admit I don't think is the case), then your Skip
usage would, in fact, make sense. orderedByCountyCustomers.Skip(5).ThenBy(c => c.Name)
–
Wawro IEnumerable
has an order to be preserved when it doesn't guarantee order (ie, is implicitly unordered)... I think it's probably better for me to think that an IEnumerable can be ordered, but doesn't, by itself, guarantee order. IOrderedEnumerable
probably isn't the best name for what it's doing either.. IInTheProcessOfBeingOrderedEnumerable
? /shrug Thanks again. –
Wawro IEnumerable<T>
have an order (for example, array index order). When they do, the order is preserved by the operations. When they don't, there is no order to preserve. All IOrderedEnumerable<T>
have a defined order. That defined order is respected and modified by calls to ThenBy
. –
Strasbourg Distinct
and not the implementation. Nothing says .Net has to run on a Von Neumann architecture - on a version for a Connection Machine, perhaps not preserving order would be a better implementation. –
Vegetal Select
remarks or anywhere else on the page that states the order is guaranteed to be preserved. –
Standford Select
function returns. Can you quote part of where you're seeing a mention of returning the items in order? –
Standford Are you actually talking about SQL, or about arrays? To put it another way, are you using LINQ to SQL or LINQ to Objects?
The LINQ to Objects operators don't actually change their original data source - they build sequences which are effectively backed by the data source. The only operations which change the ordering are OrderBy/OrderByDescending/ThenBy/ThenByDescending - and even then, those are stable for equally ordered elements. Of course, many operations will filter out some elements, but the elements which are returned will be in the same order.
If you convert to a different data structure, e.g. with ToLookup or ToDictionary, I don't believe order is preserved at that point - but that's somewhat different anyway. (The order of values mapping to the same key is preserved for lookups though, I believe.)
GroupBy
followed by SelectMany
will give the results grouped by key, but not in ascending key order... it will give them in the order in which the keys originally occurred. –
Pyrene list<x> {a b c d e f g}
if c,d,e all have the same key then the resulting sequence will contain c,d,e next to each other AND in the order c,d,e. I can't seem to find a categorical MS-based answer. –
Corcoran If you are working on an array, it sounds like you are using LINQ-to-Objects, not SQL; can you confirm? Most LINQ operations don't re-order anything (the output will be in the same order as the input) - so don't apply another sort (OrderBy[Descending]/ThenBy[Descending]).
[edit: as Jon put more clearly; LINQ generally creates a new sequence, leaving the original data alone]
Note that pushing the data into a Dictionary<,>
(ToDictionary) will scramble the data, as dictionary does not respect any particular sort order.
But most common things (Select, Where, Skip, Take) should be fine.
ToDictionary()
merely makes no promises about the order, but in practice maintains the input order (until you remove something from it). I'm not saying to rely on this, but 'scrambling' seems inaccurate. –
Codicodices I found a great answer in a similar question which references official documentation. To quote it:
For Enumerable
methods (LINQ to Objects, which applies to List<T>
), you can rely on the order of elements returned by Select
, Where
, or GroupBy
. This is not the case for things that are inherently unordered like ToDictionary
or Distinct
.
From Enumerable.GroupBy documentation:
The
IGrouping<TKey, TElement>
objects are yielded in an order based on the order of the elements in source that produced the first key of eachIGrouping<TKey, TElement>
. Elements in a grouping are yielded in the order they appear insource
.
This is not necessarily true for IQueryable
extension methods (other LINQ providers).
Source: Do LINQ's Enumerable Methods Maintain Relative Order of Elements?
Any 'group by' or 'order by' will possibly change the order.
The question here is specifically referring to LINQ-to-Objects.
If your using LINQ-to-SQL instead there is no order there unless you impose one with something like:
mysqlresult.OrderBy(e=>e.SomeColumn)
If you do not do this with LINQ-to-SQL then the order of results can vary between subsequent queries, even of the same data, which could cause an intermittant bug.
For me the issue was determining the default sort order which turned out to be by 2 columns as shown below. After many iterations I was able to find the default sort order and redo it in my LINQ query. To remove duplicates a simple foreach is used to create a new list of strings without duplicates.
//original sorting order lost
var inv2 = db.Inventories
.GroupBy(l => l.VendorFullSKU)
.Select(cl => new Inventory2
{
VariantID = cl.FirstOrDefault() == null ? 0 : cl.FirstOrDefault().VariantID,
Quan = cl.Sum(c => c.Quan),
Color = cl.FirstOrDefault() == null ? "" : cl.FirstOrDefault().Color
});
//original sorting order restored
var bl = (from pv in db.ProductVariants
join inv in inv2 on pv.VariantID equals inv.VariantID
orderby inv.VariantID, inv.Color //sort
select inv.Color
).ToList();
//remove duplicates while preserving original sort order
var colorsDistinct = new List<string>();
foreach (var item in bl)
{
if (!colorsDistinct.Contains(item))
colorsDistinct.Add(item);
}
© 2022 - 2024 — McMap. All rights reserved.