简体   繁体   中英

How to compare sequential elements in a foreach loop in C#

In a foreach loop I want to compare an element with the previous element that was read. How can I do that? What is the syntax for addressing a previous element in a foreach loop?

You don't have that option built in with a foreach loop.
You can either switch to a for loop or use a variable.

Suppose you iterate through a list of objects, these are your options:

object prev = null;
foreach(var current in myListOfObjects)
{
    if(current == prev)
    {
        // do stuff
    }

    // don't forget the next row!
    prev = current;
}

or

for(var i = 1; i < myListOfObjects.count, i++) // Note: starting from 1 to avoid another condition inside the loop.
{
    if(myListOfObjects[i] == myListOfObjects[i-1])
    {
        // do stuff
    }
}

Everything is better with Bluetooth extension methods:

public static class EnumerableExtensions
{
    public struct CurrentAndPrevious<T>
    {
        public T Current { get; private set; }
        public T Previous { get; private set; }

        public CurrentAndPrevious(T current, T previous) : this()
        {
            Previous = previous;
            Current = current;
        }
    }

    public static IEnumerable<CurrentAndPrevious<T>> WithPrevious<T>(this IEnumerable<T> enumerable)
    {
        var previous = default(T);

        using(var enumerator = enumerable.GetEnumerator())
        {
            while(enumerator.MoveNext())
            {
                yield return new CurrentAndPrevious<T>(enumerator.Current, previous);
                previous = enumerator.Current;
            }
        }
    }
}

var items = new[] { 1, 2, 3, 4, 5 };
foreach(var item in items.WithPrevious())
{
    Console.WriteLine(item.Previous + " " + item.Current);
}

You might need to tweak this depending on how you want first and last elements handled.

You can loop over a bit modified source instead of initial, say ListOfMyObjects :

MyObject prior = default(MyObject);

var source = ListOfMyObjects
  .Select(item => {
     var result = new {
       Current = item,
       Prior = prior, 
     };

     prior = item; // side effect, not a good practice

     return result;  
  });

So you can loop

foreach(var item in source) {
  if (item.Prior == item.Current) {
    ...
  }
}

A foreach itself has no syntax 'for addressing a previous element'. There are two options, depending on the characteristics of the collection and also the notion of a 'previous' element in respect of the first one. The following the examples are a little bit simplistic, but you should be able to choose the right path and fine-tune the details.

Option 1: Use a temporary variable

Works well if there's no cheap (performance-wise) way to index elements in the sequence, and you are OK with 'pretending' there's an empty ( null , or default(T) ) item before the very first item.

T previous = default(T);  // corresponds to null for reference types
foreach (T item in sequence)
{
     … work with previous and item here…

     // the current 'item' is the new 'previous' for the next iteration
     previous = item;
}

Note that if T is a value type, your would be actually copying the values themselves.

Option 2: Use a for loop and indexing

Works well if there is a cheap (performance-wise) way to index individual elements directly. List<T> and arrays are good examples here.

// indexing from 1, i.e. from the second item in the sequence
for (int i = 1; i < sequence.Count; i++)
{
    var previous = sequence[i-1]; // this is obviously the previous item
    var current = sequence[i];    // this is obviously the current item
}

Similar to using a temp variable, however this solution moves the scope of the temp variable inside the loop

var collection = new List<int>() { 1, 2, 3, 4, 5 };

foreach (var item in collection)
{
    var currentIndex = collection.IndexOf(item);
    if (currentIndex > 0 && currentIndex < collection.Count)
    {
        var previousItem = collection[currentIndex - 1];
    }
}

As mentioned by Pham X, one easy way to do this would be a temp variable.

ObjectType temp_object = null;
foreach(var entry in ListOfObjects)
{
    if(temp_object==null)
    {
        //this is the first time through...
        temp_object=entry;
    }
    else
    {
        //it's anything after the first loop
        if(entry==temp_object) Console.WriteLine("There is a match between two entries.");
        else temp_object=entry;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM