Preserving Order With Linq

Preserving order with LINQ

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;
}

Does Select linq function preserve List collection order?

.net is open sourced now. Check it out for IList First() just takes first element. And Select just enumerates list, so order is preserved.


As for your particular First(x => x.Item1) case it will turn into.

foreach (TSource element in source) {
if (element.Item1) return element;
}

where source is similar to that IEnumberable (I've removed some code, which is not used in your case)

public Func<int, Tuple<bool, string>> Current {
get { return current; }
}

public override bool MoveNext() {
var enumerator = conditions.GetEnumerator();
while (enumerator.MoveNext()) {
Func<int, Tuple<bool, string>> item = enumerator.Current;
current = item(t);
return true;
}
return false;
}

Does Enumerable.Where in LINQ-to-objects preserve order?

Microsoft does actually document that LINQ to Objects preserves ordering. The document
http://msdn.microsoft.com/en-us/library/dd460677%28v=vs.110%29.aspx says

In PLINQ, the goal is to maximize performance while maintaining
correctness. A query should run as fast as possible but still produce
the correct results. In some cases, correctness requires the order of
the source sequence to be preserved; however, ordering can be
computationally expensive. Therefore, by default, PLINQ does not
preserve the order of the source sequence. In this regard, PLINQ
resembles LINQ to SQL, but is unlike LINQ to Objects, which does
preserve ordering.

As mentioned in this stackoverflow article microsoft documents for some LINQ methods that they do not preserve order. For example the documentation of distinct mentions that this method returns an unordered sequence.

Why LINQ Distinct() does not preserve order list?

Distinct() is about returning distinct rows, it doesn't promise any order but still orders on what it operates. For ordering there is OrderBy, OrderByDescending. In your case, since there is no Date in your final list, you cannot order by date.

EDIT:

void Main()
{
List<FarmDiary> farmDiary = new List<FarmDiary> {
new FarmDiary{Id=1, PersonInCharge="Bob",Date=new DateTime(2018,6,1)},
new FarmDiary{Id=2, PersonInCharge="John",Date=new DateTime(2018,6,2)},
new FarmDiary{Id=3, PersonInCharge="Bob",Date=new DateTime(2018,6,15)},
new FarmDiary{Id=4, PersonInCharge="David",Date=new DateTime(2018,7,1)},
new FarmDiary{Id=5, PersonInCharge="Zachary",Date=new DateTime(2018,6,10)},
};

List<string> staffNames = farmDiary
.GroupBy(d => d.PersonInCharge)
.OrderByDescending(x => x.OrderByDescending(d => d.Date).First().Date)
.Select(x => x.Key)
.ToList();

staffNames.Dump();
}

public class FarmDiary
{
public int Id { get; set; }
public string PersonInCharge { get; set; }
public DateTime Date { get; set; }
}

Preserve order with linq after groupby and selectmany

Yes, if you first select your DateList and combine it with an index, using an overload of .Select that uses a delegate with a second (int) parameter that is called with the index of the items from the sequence :

DateList
.Select((dateTime, idx) => new {dateTime, idx})
.GroupBy(x => x.dateTime.Date.Subtract(firstDay).Days / 7 + 1)

...and persist the value through the linq chain

    .SelectMany(gx => gx, (gx, x) => new {Week =  gx.Key,
DateTime = x.dateTime,
Count = gx.Count(),
x.idx})

...then use it to re-order the output

    .OrderBy(x => x.idx)

...and strip it from your final selection

    .Select(x => new {x.Week, x.DateTime, x.Count});

then you can maintain the same order as the original list.

Linq to Objects: does GroupBy preserve order of elements?

Found answer on MSDN: Yes.

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.


Retaining order with LINQ using .Contains

If I understand correctly, contentBasedFiltered is the ordered list and you want the original items from restaurantsCloseToUser but in the order they have in contentBasedFiltered. So I'd try the following:

var test = contentBasedFiltered.Select(x => restaurantsCloseToUser.FirstOrDefault(r => r.Id == x.Id)).ToList();

This returns a list in order of contentBasedFiltered but with the items from restaurantsCloseToUser.

LINQ - Preserve order across objects with .Any()?

You need to do it the other way around:

Query orderedProjects and select the corresponding items from projects:

var projects = 
orderedProjects
.Select(o => projects.SingleOrDefault(p => p.ProjectNbr == o.ProjectNbr))
.Where(x => x != null) // This is only necessary if there can be
// ProjectNbrs in orderedProjects that are not in
// projects
.ToList();

Does LINQ to Objects keep its order

In current .Net implementation it use such code. But there are no guarantee that this implementation will be in future.

private static IEnumerable<TResult> SelectIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, int, TResult> selector)
{
int index = -1;
foreach (TSource source1 in source)
{
checked { ++index; }
yield return selector(source1, index);
}
}

Retain default order for Linq Contains

Your assumption is wrong. SQL server is the one that is sending the results back in the order you are getting them. However, you can fix that:

var x = db.ItemTemplates.Where(a => a.MainGroupId == mnId)
.Where(a => a.SubGruopId == sbId)
.FirstOrDefault();
var ids = new List<int> { x.Atribute1, x.Atribute2, x.Atribute3, x.Atribute4 };
var y = db.Atributes.Where(a => ids.Contains(a.AtributeId))
.Select(g => new
{
Id = g.AtributeId,
Name = g.AtributeName,
AtType = g.AtributeType,
Options = g.atributeDetails
.Where(w=>w.AtributeDetailId!=null)
.Select(z => new
{
Value=z.AtributeDetailId,
Text=z.AtDetailVal
})
})
.ToList()
.OrderBy(z=>ids.IndexOf(z.Id));

Feel free to do another select after the orderby to create a new anonymous object without the Id if you absolutely need it to not contain the id.

PS. You might want to correct the spelling of Attribute, and you should be consistent in if you are going to prefix your property names, and how you do so. Your table prefixes everything with Atribute(sp?), and then when you go and cast into your anonymous object, you remove the prefix on all the properties except AtributeType, which you prefix with At. Pick one and stick with it, choose AtName, AtType, AtOptions or Name, Type, Options.



Related Topics



Leave a reply



Submit