tags:

views:

2732

answers:

14

I need to move backwards through an array, so I have code like this:

for (int i = myArray.Length - 1; i >= 0; i--)
{
    // Do something
    myArray[i] = 42;
}

Is there a better way of doing this?

Update: I was hoping that maybe C# had some built-in mechanism for this like:

foreachbackwards (int i in myArray)
{
    // so easy
}

Update 2: There are better ways. Rune takes the prize with:

for (int i = myArray.Length; i-- > 0; )
{    
    //do something
}
//or
for (int i = myArray.Length; i --> 0; )
{
    // do something
}

which looks even better in regular C (thanks to Twotymz):

for (int i = lengthOfArray; i--; )
{    
    //do something
}
A: 

No, that's pretty good.

JXG
+8  A: 

That's definitely the best way for any array whose length is a signed integral type. For arrays whose lengths are an unsigned integral type (e.g. an std::vector in C++), then you need to modify the end condition slightly:

for(size_t i = myArray.size() - 1; i != (size_t)-1; i--)
    // blah

If you just said i >= 0, this is always true for an unsigned integer, so the loop will be an infinite loop.

Adam Rosenfield
In C++ the "i--" would automatically wrap around to the highest value if i was 0? Sorry, I'm C++ impaired.
MusiGenesis
Amazing. You guys just helped me understand the cause of a hard-drive filling bug in something I wrote 12 years ago. Good thing I don't work in C++.
MusiGenesis
termination condition should be: i < myArray.size(). Only depends on overflow properties of unsigned int; not implementation details of integers and casts. Consequently easier to read and works in languages with alternate integer representations.
ejgottl
I think a better solution for me is to stay in the C# world where I can't hurt anybody.
MusiGenesis
+2  A: 

Looks good to me. If the indexer was unsigned (uint etc), you might have to take that into account. Call me lazy, but in that (unsigned) case, I might just use a counter-variable:

uint pos = arr.Length;
for(uint i = 0; i < arr.Length ; i++)
{
    arr[--pos] = 42;
}

(actually, even here you'd need to be careful of cases like arr.Length = uint.MaxValue... maybe a != somewhere... of course, that is a very unlikely case!)

Marc Gravell
The "--pos" thing is tricky, though. I've had coworkers in the past who were fond of randomly "correcting" things in code that didn't look quite right to them.
MusiGenesis
Then use .arr.Length-1 and pos--
Marc Gravell
A: 

I'm going to try answering my own question here, but I don't really like this, either:

for (int i = 0; i < myArray.Length; i++)
{
    int iBackwards = myArray.Length - 1 - i; // ugh
    myArray[iBackwards] = 666;
}
MusiGenesis
Rather than do the .Length - 1 - i each time, perhaps consider a second variable? See my [updated] post.
Marc Gravell
Yours is much prettier, thanks.
MusiGenesis
Down-voted on my own question. Harsh. It's not any clunkier than the original.
MusiGenesis
+7  A: 

In C# using Linq:

foreach(var item in myArray.Reverse())
{
    // do something
}
Keltex
This is for sure the simplest, but note that in the current version of the CLR reversing a list is always an O(n) operation rather than the O(1) operation it could be if it is an IList<T>; see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=355096
Greg Beech
Looks like .Reverse() makes a local copy of the array and then iterates backwards through it. It seems kind of inefficient to copy an array just so you can iterate through it. I guess any post with "Linq" in it is worthy of up-votes. :)
MusiGenesis
There is a possible tradeoff, but then you run into the issue that the "voted on answer" (i --> 0) might result in slower code because (i) has to be checked against the size of the array each time it is used as in index.
Keltex
+24  A: 

In C++ you basicially have the choice between iterating using iterators, or indices. Depending on whether you have a plain array, or a std::vector, you use different techniques.

Using std::vector

Using iterators

C++ allows you to do this using std::reverse_iterator:

for(std::vector<T>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it) {
    /* std::cout << *it; ... */
}

Using indices

The unsigned integral type returned by std::vector::size is not always std::size_t . It can be greater or less. This is crucial for the loop to work.

for(std::vector<int>::size_type i = someVector.size() - 1; 
    i != (std::vector<int>::size_type) -1; i--) {
    /* std::cout << someVector[i]; ... */
}

It works, since unsigned integral types values are defined by means of modulo their count of bits. Thus, if you are setting -N, you end up at (2 ^ BIT_SIZE) -N

Using Arrays

Using iterators

We are using std::reverse_iterator to do the iterating.

for(std::reverse_iterator<element_type*> it(a + sizeof a / sizeof *a), itb(a); 
    it != itb; 
    ++it) {
    /* std::cout << *it; .... */
}

Using indices

We can safely use std::size_t here, as opposed to above, since sizeof always returns std::size_t by definition.

for(std::size_t i = (sizeof a / sizeof *a) - 1; i != (std::size_t) -1; i--) {
   /* std::cout << a[i]; ... */
}

Avoiding pitfalls with sizeof applied to pointers

Actually the above way of determining the size of an array sucks. If a is actually a pointer instead of an array (which happens quite often, and beginners will confuse it), it will silently fail. A better way is to use the following, which will fail at compile time, if given a pointer:

template<typename T, std::size_t N> char (& array_size(T(&)[N]) )[N];

It works by getting the size of the passed array first, and then declaring to return a reference to an array of type char of the same size. char is defined to have sizeof 1. So the returned array will have a sizeof of N * 1, which is what we are looking for, with only compile time evaluation and zero runtime overhead.

Instead of doing

(sizeof a / sizeof *a)

Change your code so that it now does

(sizeof array_size(a))
Johannes Schaub - litb
Additionally, if your container doesn't have a reverse iterator, you can use boost's reverse_iterator implementation: http://www.boost.org/doc/libs/1_36_0/libs/iterator/doc/reverse_iterator.html
MP24
your array_size seems to work for statically allocated arrays, but failed for me when a was 'new int[7]' for example.
drhorrible
yeah that is the intent of it :) new int[7] returns a pointer. so sizeof(new int[7]) returns not 7 * sizeof(int) , but would return sizeof(int*) . array_size causes a compile error for that case instead of silently working.
Johannes Schaub - litb
also try array_size(+statically_allocated_array) , which also fails, because the + operator makes the array into a pointer to its first element. using plain sizeof would give you the size of a pointer again
Johannes Schaub - litb
+13  A: 

While admittedly a bit obscure, I would say that the most typographically pleasing way of doing this is

for (int i = myArray.Length; i --> 0; )
{
    //do something
}
When I first read your answer it looked like it wouldn't even compile, so I assumed you were a crazy person. But it's exactly what I was looking for: a better way of writing a backwards for loop.
MusiGenesis
I think that i --> 0; is on purpose. That's what he means by "typographically pleasing"
Johannes Schaub - litb
I found it "typographically confusing" myself. For me, the "--" doesn't look right unless it's adjacent to the variable it's affecting.
MusiGenesis
I changed his version back. It does look better esthetically that way, but it also looks more like it isn't even valid code.
MusiGenesis
That's pretty cool - Not sure if it would be a good idea to use, but still quite cool
Hugoware
It looks like the i is pointing to the 0, but yes, it works!
drhorrible
This is pretty good but also confusing.
Dmitri Nesteruk
I prefer this: `for (int i = myArray.Length; i-->0 ;)` - I think that would score one extra WTF point.
Hamish Grubijan
A: 

NOTE: This post ended up being far more detailed and therefore off topic, I apologize.

That being said my peers read it and believe it is valuable 'somewhere'. This thread is not the place. I would appreciate your feedback on where this should go (I am new to the site).


Anyway this is the C# version in .NET 3.5 which is amazing in that it works on any collection type using the defined semantics. This is a default measure (reuse!) not performance or CPU cycle minimization in most common dev scenario although that never seems to be what happens in the real world (premature optimization).

*** Extension method working over any collection type and taking an action delegate expecting a single value of the type, all executed over each item in reverse **

Requres 3.5:

public static void PerformOverReversed<T>(this IEnumerable<T> sequenceToReverse, Action<T> doForEachReversed)
      {
          foreach (var contextItem in sequenceToReverse.Reverse())
              doForEachReversed(contextItem);
      }

Older .NET versions or do you want to understand Linq internals better? Read on.. Or not..

ASSUMPTION: In the .NET type system the Array type inherits from the IEnumerable interface (not the generic IEnumerable only IEnumerable).

This is all you need to iterate from beginning to end, however you want to move in the opposite direction. As IEnumerable works on Array of type 'object' any type is valid,

CRITICAL MEASURE: We assume if you can process any sequence in reverse order that is 'better' then only being able to do it on integers.

Solution a for .NET CLR 2.0-3.0:

Description: We will accept any IEnumerable implementing instance with the mandate that each instance it contains is of the same type. So if we recieve an array the entire array contains instances of type X. If any other instances are of a type !=X an exception is thrown:

A singleton service:

public class ReverserService { private ReverserService() { }

    /// <summary>
    /// Most importantly uses yield command for efficiency
    /// </summary>
    /// <param name="enumerableInstance"></param>
    /// <returns></returns>
    public static IEnumerable ToReveresed(IEnumerable enumerableInstance)
    {
        if (enumerableInstance == null)
        {
            throw new ArgumentNullException("enumerableInstance");
        }

        // First we need to move forwarad and create a temp
        // copy of a type that allows us to move backwards
        // We can use ArrayList for this as the concrete
        // type

        IList reversedEnumerable = new ArrayList();
        IEnumerator tempEnumerator = enumerableInstance.GetEnumerator();

        while (tempEnumerator.MoveNext())
        {
            reversedEnumerable.Add(tempEnumerator.Current);
        }

        // Now we do the standard reverse over this using yield to return
        // the result
        // NOTE: This is an immutable result by design. That is 
        // a design goal for this simple question as well as most other set related 
        // requirements, which is why Linq results are immutable for example
        // In fact this is foundational code to understand Linq

        for (var i = reversedEnumerable.Count - 1; i >= 0; i--)
        {
            yield return reversedEnumerable[i];
        }
    }
}



public static class ExtensionMethods
{

      public static IEnumerable ToReveresed(this IEnumerable enumerableInstance)
      {
          return ReverserService.ToReveresed(enumerableInstance);
      }
 }

[TestFixture] public class Testing123 {

    /// <summary>
    /// .NET 1.1 CLR
    /// </summary>
    [Test]
    public void Tester_fornet_1_dot_1()
    {
        const int initialSize = 1000;

        // Create the baseline data
        int[] myArray = new int[initialSize];

        for (var i = 0; i < initialSize; i++)
        {
            myArray[i] = i + 1;
        }

        IEnumerable _revered = ReverserService.ToReveresed(myArray);

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));
    }

    [Test]
    public void tester_why_this_is_good()
    {

        ArrayList names = new ArrayList();
        names.Add("Jim");
        names.Add("Bob");
        names.Add("Eric");
        names.Add("Sam");

        IEnumerable _revered = ReverserService.ToReveresed(names);

        Assert.IsTrue(TestAndGetResult(_revered).Equals("Sam"));


    }

    [Test]
    public void tester_extension_method()
  {

        // Extension Methods No Linq (Linq does this for you as I will show)
        var enumerableOfInt = Enumerable.Range(1, 1000);

        // Use Extension Method - which simply wraps older clr code
        IEnumerable _revered = enumerableOfInt.ToReveresed();

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));


    }


    [Test]
    public void tester_linq_3_dot_5_clr()
    {

        // Extension Methods No Linq (Linq does this for you as I will show)
        IEnumerable enumerableOfInt = Enumerable.Range(1, 1000);

        // Reverse is Linq (which is are extension methods off IEnumerable<T>
        // Note you must case IEnumerable (non generic) using OfType or Cast
        IEnumerable _revered = enumerableOfInt.Cast<int>().Reverse();

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));


    }



    [Test]
    public void tester_final_and_recommended_colution()
    {

        var enumerableOfInt = Enumerable.Range(1, 1000);
        enumerableOfInt.PerformOverReversed(i => Debug.WriteLine(i));

    }



    private static object TestAndGetResult(IEnumerable enumerableIn)
    {
      //  IEnumerable x = ReverserService.ToReveresed(names);

        Assert.IsTrue(enumerableIn != null);
        IEnumerator _test = enumerableIn.GetEnumerator();

        // Move to first
        Assert.IsTrue(_test.MoveNext());
        return _test.Current;
    }
}
domain.dot.net team
Dude or dudette: I was just looking for a less-clunky way of writing "for (int i = myArray.Length - 1; i >= 0; i--)".
MusiGenesis
A: 

I'd use the code in the original question, but if you really wanted to use foreach and have an integer index in C#:

foreach (int i in Enumerable.Range(0, myArray.Length).Reverse())
{
    myArray[i] = 42; 
}
frou
+1  A: 

In C I like to do this:


int i = myArray.Length;
while (i--) {
  myArray[i] = 42;
}

C# example added by MusiGenesis:

{int i = myArray.Length; while (i-- > 0)
{
    myArray[i] = 42;
}}
Twotymz
I like this. It took me a second or two to realize that your method works. Hope you don't mind the added C# version (the extra braces are so that the index is scoped the same as in a for loop).
MusiGenesis
I'm not sure I would use this in production code, because it would elicit a universal "WTF is this?" reaction from whoever had to maintain it, but it's actually easier to type than a normal for loop, and no minus signs or >= symbols.
MusiGenesis
Too bad the C# version needs the extra " > 0 ".
MusiGenesis
I'm not sure what language it is. but your first code snippet is certainly NOT C :) just to tell you the obvious. maybe you wanted to write C# and html failed to parse it or something?
Johannes Schaub - litb
@litb: I guess the "myArray.Length" isn't valid C (?), but the while loop part works and looks great.
MusiGenesis
MusiGenesis: indeed it's great. short and to the point
Johannes Schaub - litb
Oops. Your correct the initialization of i is not C. I was just trying to use the OP original snippet to illustrate the point.
Twotymz
+5  A: 

In C#, using Visual Studio 2005 or later, type 'forr' and hit [TAB] [TAB]. This will expand to a for loop that goes backwards through a collection.

It's so easy to get wrong (at least for me), that I thought putting this snippet in would be a good idea.

That said, I like Array.Reverse() / Enumerable.Reverse() and then iterate forwards better - they more clearly state intent.

Jay Bazuzi
That just saved me a lot of pain. Thank you.
MusiGenesis
A: 

I'm not sure I see why any of the alternatives is better, if goodness includes clarity or maintainability.

le dorfier
True. I was hoping to find a more reasonable way to do this, since I have to do it fairly often.
MusiGenesis
+2  A: 

The best way to do that in C++ is probably to use iterator (or better, range) adaptors, which will lazily transform the sequence as it is being traversed.

Basically,

vector<value_type> range;
foreach(value_type v, range | reversed)
    cout << v;

Displays the range "range" (here, it's empty, but i'm fairly sure you can add elements yourself) in reverse order. Of course simply iterating the range is not much use, but passing that new range to algorithms and stuff is pretty cool.

This mechanism can also be used for much more powerful uses:

range | transformed(f) | filtered(p) | reversed

Will lazily compute the range "range", where function "f" is applied to all elements, elements for which "p" is not true are removed, and finally the resulting range is reversed.

Pipe syntax is the most readable IMO, given it's infix. The Boost.Range library update pending review implements this, but it's pretty simple to do it yourself also. It's even more cool with a lambda DSEL to generate the function f and the predicate p in-line.

can you tell us where you have "foreach" from? Is it a #define to BOOST_FOREACH ? Looks cute all the way.
Johannes Schaub - litb
A: 
// this is how I always do it
for (i = n; --i >= 0;){
   ...
}
Mike Dunlavey