How to check if a list is ordered?

I am doing some unit tests and I want to know if there is a way to check if the list is ordered by the property of the objects that it contains.

Now I do it this way, but I donโ€™t like it, I want better. Can someone help me?

// (fill the list) List<StudyFeedItem> studyFeeds = Feeds.GetStudyFeeds(2120, DateTime.Today.AddDays(-200), 20); StudyFeedItem previous = studyFeeds.First(); foreach (StudyFeedItem item in studyFeeds) { if (item != previous) { Assert.IsTrue(previous.Date > item.Date); } previous = item; } 
+55
list c # html-lists unit-testing
Dec 21 '09 at 13:54
source share
21 answers

If you are using MSTest, you can take a look at CollectionAssert.AreEqual .

Enumerable.SequenceEqual may be another useful API to use in a statement.

In both cases, you must prepare a list that contains the expected list in the expected order, and then compare this list with the result.

Here is an example:

 var studyFeeds = Feeds.GetStudyFeeds(2120, DateTime.Today.AddDays(-200), 20); var expectedList = studyFeeds.OrderByDescending(x => x.Date); Assert.IsTrue(expectedList.SequenceEqual(studyFeeds)); 
+59
Dec 21 '09 at 14:03
source share

The .NET 4.0 method would have to use the Enumerable.Zip method to fasten the list, while itself will be shifted by one, which links each element to the next item in the list. Then you can verify that the condition is true for each pair, for example.

 var ordered = studyFeeds.Zip(studyFeeds.Skip(1), (a, b) => new { a, b }) .All(p => paDate < pbDate); 

If you are using an earlier version of the framework, you can write your own Zip method without any problems, something like the following (checking the argument and deleting counters, if applicable, provided to the reader):

 public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>( this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> selector) { var e1 = first.GetEnumerator(); var e2 = second.GetEnumerator(); while (e1.MoveNext() & e2.MoveNext()) // one & is important yield return selector(e1.Current, e2.Current); } 
+37
Dec 21 '09 at 14:04
source share

If your unit testing platform has helper methods for confirming equality of collections, you should do something like this (NUnit flavored):

 var sorted = studyFeeds.OrderBy(s => s.Date); CollectionAssert.AreEqual(sorted.ToList(), studyFeeds.ToList()); 

The assert method works with any IEnumerable , but when both collections are of type IList or an "array of something", the error message that is generated when the statement fails will contain the index of the first out-of-place element.

+26
Dec 21 '09 at 14:02
source share

Nunit 2.5 introduced CollectionOrderedContraint and good syntax for checking collection order:

 Assert.That(collection, Is.Ordered.By("PropertyName")); 

No need to manually order and compare.

+23
Oct 23 '13 at 13:58
source share

Decisions related to sorting a list, roads โ€” determining whether a sorted list can be sorted in O (N). Here's an extension method that will check:

 public static bool IsOrdered<T>(this IList<T> list, IComparer<T> comparer = null) { if (comparer == null) { comparer = Comparer<T>.Default; } if (list.Count > 1) { for (int i = 1; i < list.Count; i++) { if (comparer.Compare(list[i - 1], list[i]) > 0) { return false; } } } return true; } 

The corresponding IsOrderedDescending can be easily implemented by changing > 0 to < 0 .

+11
Jan 11 '16 at 11:47
source share
 if(studyFeeds.Length < 2) return; for(int i = 1; i < studyFeeds.Length;i++) Assert.IsTrue(studyFeeds[i-1].Date > studyFeeds[i].Date); 

for not dead yet!

+9
Dec 21 '09 at 2:00
source share

What about:

 var list = items.ToList(); for(int i = 1; i < list.Count; i++) { Assert.IsTrue(yourComparer.Compare(list[i - 1], list[i]) <= 0); } 

where yourComparer is an instance of yourComparer that implements IComparer<YourBusinessObject> . This ensures that each element is smaller than the next element in the listing.

+7
Nov 04 '09 at 19:57
source share

The Greg Beech answer , although excellent, can be simplified by running the test in Zip itself. Therefore, instead of:

 var ordered = studyFeeds.Zip(studyFeeds.Skip(1), (a, b) => new { a, b }) .All(p => paDate < pbDate); 

You can simply do:

 var ordered = !studyFeeds.Zip(studyFeeds.Skip(1), (a, b) => a.Date < b.Date) .Contains(false); 

Which saves you one lambda expression and one anonymous type.

(In my opinion, deleting an anonymous type also makes reading easier.)

+7
Sep 15 '15 at 5:54 on
source share

Linq based answer:

You can use the SequenceEqual method to check if the source and ordered are the same or not.

 var isOrderedAscending = lJobsList.SequenceEqual(lJobsList.OrderBy(x => x)); var isOrderedDescending = lJobsList.SequenceEqual(lJobsList.OrderByDescending(x => x)); 

Remember to import the System.Linq namespace.

Additionally:

I repeat that this answer is based on Linq, you can achieve greater efficiency by creating your own extension method.

Also, if someone still wants to use Linq and check if the sequence is ordered in ascending or descending order, then you can achieve even greater efficiency:

 var orderedSequence = lJobsList.OrderBy(x => x) .ToList(); var reversedOrderSequence = orderedSequence.AsEnumerable() .Reverse(); if (lJobsList.SequenceEqual(orderedSequence)) { // Ordered in ascending } else (lJobsList.SequenceEqual(reversedOrderSequence)) { // Ordered in descending } 
+6
Jan 11 '16 at 11:43
source share

This is how I do it with Linq, and I'm comparable, maybe not the best, but it works for me, and it is independent of verification.

So the call is as follows:

  myList.IsOrderedBy(a => a.StartDate) 

This works for everything that implements IComparable, so strings of numbers and everything that inherits from IComparable:

  public static bool IsOrderedBy<T, TProperty>(this List<T> list, Expression<Func<T, TProperty>> propertyExpression) where TProperty : IComparable<TProperty> { var member = (MemberExpression) propertyExpression.Body; var propertyInfo = (PropertyInfo) member.Member; IComparable<TProperty> previousValue = null; for (int i = 0; i < list.Count(); i++) { var currentValue = (TProperty)propertyInfo.GetValue(list[i], null); if (previousValue == null) { previousValue = currentValue; continue; } if(previousValue.CompareTo(currentValue) > 0) return false; previousValue = currentValue; } return true; } 

Hope this helps, it took me years to work on this.

+4
Oct 26 '11 at 15:58
source share

You can use the extension method as follows:

 public static System.ComponentModel.ListSortDirection? SortDirection<T>(this IEnumerable<T> items, Comparer<T> comparer = null) { if (items == null) throw new ArgumentNullException("items"); if (comparer == null) comparer = Comparer<T>.Default; bool ascendingOrder = true; bool descendingOrder = true; using (var e = items.GetEnumerator()) { if (e.MoveNext()) { T last = e.Current; // first item while (e.MoveNext()) { int diff = comparer.Compare(last, e.Current); if (diff > 0) ascendingOrder = false; else if (diff < 0) descendingOrder = false; if (!ascendingOrder && !descendingOrder) break; last = e.Current; } } } if (ascendingOrder) return System.ComponentModel.ListSortDirection.Ascending; else if (descendingOrder) return System.ComponentModel.ListSortDirection.Descending; else return null; } 

Allows you to check whether the sequence is sorted, and also determines the direction:

 var items = new[] { 3, 2, 1, 1, 0 }; var sort = items.SortDirection(); Console.WriteLine("Is sorted? {0}, Direction: {1}", sort.HasValue, sort); //Is sorted? True, Direction: Descending 
+4
Jan 11 '16 at 11:49
source share

Sequencing can have four different results. Same means that all elements in the sequence are the same (or the sequence is empty):

 enum Sort { Unsorted, Same, SortedAscending, SortedDescending } 

Here is a way to check the sorting sequence:

 Sort GetSort<T>(IEnumerable<T> source, IComparer<T> comparer = null) { if (source == null) throw new ArgumentNullException(nameof(source)); if (comparer == null) comparer = Comparer<T>.Default; using (var enumerator = source.GetEnumerator()) { if (!enumerator.MoveNext()) return Sort.Same; Sort? result = null; var previousItem = enumerator.Current; while (enumerator.MoveNext()) { var nextItem = enumerator.Current; var comparison = comparer.Compare(previousItem, nextItem); if (comparison < 0) { if (result == Sort.SortedDescending) return Sort.Unsorted; result = Sort.SortedAscending; } else if (comparison > 0) { if (result == Sort.SortedAscending) return Sort.Unsorted; result = Sort.SortedDescending; } } return result ?? Sort.Same; } } 

I use the enumerator directly instead of the foreach , because I need to check the elements of the sequence as pairs. This makes the code more complex, but also more efficient.

+2
Jan 11 '16 at 11:56 on
source share

Something LINQ-y will use a separate sorted query ...

 var sorted = from item in items orderby item.Priority select item; Assert.IsTrue(items.SequenceEquals(sorted)); 

Type inference means you need

  where T : IHasPriority 

However, if you have multiple items with the same priority, then for the unit test statement, you are probably best off looping around the list index, as Jason suggested.

+1
Nov 04 '09 at 20:14
source share

One way or another, you have to go through the list and make sure that the elements are in the order you want. Since comparing elements is common, you can study creating a common method for this and passing in comparison functions - just like sorting a list uses comparison functions.

+1
Dec 21 '09 at 13:59
source share
 var studyFeeds = Feeds.GetStudyFeeds(2120, DateTime.Today.AddDays(-200), 20); var orderedFeeds = studyFeeds.OrderBy(f => f.Date); for (int i = 0; i < studyFeeds.Count; i++) { Assert.AreEqual(orderedFeeds[i].Date, studyFeeds[i].Date); } 
0
Dec 21 '09 at 13:59
source share

Something like this without sorting the list

  public static bool IsAscendingOrder<T>(this IEnumerable<T> seq) where T : IComparable { var seqArray = seq as T[] ?? seq.ToArray(); return !seqArray.Where((e, i) => i < seqArray.Count() - 1 && e.CompareTo(seqArray.ElementAt(i + 1)) >= 0).Any(); } 
0
Nov 07 '14 at 10:43
source share
 Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert.AreEqual( mylist.OrderBy((a) => a.SomeProperty).ToList(), mylist, "Not sorted."); 
0
Apr 09 '15 at 13:43 on
source share

Here is a lighter generic version. To check the descending order, change the comparison> = 0 to <= 0.

 public static bool IsAscendingOrder<T>(this IEnumerable<T> seq) where T : IComparable<T> { var predecessor = default(T); var hasPredecessor = false; foreach(var x in seq) { if (hasPredecessor && predecessor.CompareTo(x) >= 0) return false; predecessor = x; hasPredecessor = true; } return true; } 

Tests:

  • new int [] {} .IsAscendingOrder () returns true
  • new int [] {1} .IsAscendingOrder () returns true
  • new int [] {1,2} .IsAscendingOrder () returns true
  • new int [] {1,2,0} .IsAscendingOrder () returns false
0
Oct 18 '15 at 22:25
source share

While the answers of AnorZaken and Greg Beech are very nice, since they do not require the use of the extension method, it is sometimes useful to avoid Zip (), as some enumerations can be expensive to enumerate in this way.

The solution can be found in Aggregate ()

 double[] score1 = new double[] { 12.2, 13.3, 5, 17.2, 2.2, 4.5 }; double[] score2 = new double[] { 2.2, 4.5, 5, 12.2, 13.3, 17.2 }; bool isordered1 = score1.Aggregate(double.MinValue,(accum,elem)=>elem>=accum?elem:double.MaxValue) < double.MaxValue; bool isordered2 = score2.Aggregate(double.MinValue,(accum,elem)=>elem>=accum?elem:double.MaxValue) < double.MaxValue; Console.WriteLine ("isordered1 {0}",isordered1); Console.WriteLine ("isordered2 {0}",isordered2); 

One thing a little ugly regarding the above solution is the double comparison less. Floating comparisons like this make me nauseous as it is almost like comparing floating point equality. But it seems he works here twice. Integer values โ€‹โ€‹will also be exact. Floating point comparisons can be avoided by using types with a null value, but then the code becomes a little harder to read.

 double[] score3 = new double[] { 12.2, 13.3, 5, 17.2, 2.2, 4.5 }; double[] score4 = new double[] { 2.2, 4.5, 5, 12.2, 13.3, 17.2 }; bool isordered3 = score3.Aggregate((double?)double.MinValue,(accum,elem)=>(elem>(accum??(double?)double.MaxValue).Value)?(double?)elem:(double?)null) !=null; bool isordered4 = score4.Aggregate((double?)double.MinValue,(accum,elem)=>(elem>(accum??(double?)double.MaxValue).Value)?(double?)elem:(double?)null) !=null; Console.WriteLine ("isordered3 {0}",isordered3); Console.WriteLine ("isordered4 {0}",isordered4); 
0
Oct 22 '15 at 1:32
source share

First you can create an ordered and unordered version of the list:

 var asc = jobs.OrderBy(x => x); var desc = jobs.OrderByDescending(x => x); 

Now compare the source list with both:

 if (jobs.SequenceEqual(asc) || jobs.SequenceEquals(desc)) // ... 
0
Jan 11 '16 at 11:47
source share

You can use lambda in the extension:

 public static bool IsAscending<T>(this IEnumerable<T> self, Func<T, T, int> compareTo) { var list = self as IList<T> ?? self.ToList(); for (int i = 1; i < list.Count; i++) { if (compareTo(list[i - 1], list[i]) > 0) { return false; } } return true; } 

Using:

  bool result1 = Enumerable.Range(2, 10).IsAscending((a, b) => a.CompareTo(b)); var lst = new List<(int, string)> { (1, "b"), (2, "a"), (3, "s1"), (3, "s") }; bool result2 = lst.IsAscending((a, b) => { var cmp = a.Item1.CompareTo(b.Item1); if (cmp != 0) { return cmp; } else { return a.Item2.CompareTo(b.Item2); } }); 
0
Aug 28 '19 at 8:44
source share



All Articles