9

I have a for loop where I must skip the first element in a zero-based array.

Which of these shows my intentions more clearly?

for($i=1 ; $i < count(array) ; $i++){
    array[$i];
}

or

for($i=0+1 ; $i < count(array) ; $i++){
    array[$i];
}
  • 27
    Neither, $i=2-1 is the superior way. :/ – yannis Oct 7 '16 at 16:08
  • 7
    I'd probably go for the first option and add a comment to explain why the first element has to be skipped. – Vincent Savard Oct 7 '16 at 16:15
  • 3
    Clearness is what I am aiming for, I edited the question. Is that any clearer? – Tomas Zubiri Oct 7 '16 at 17:51
  • 9
    Not using PHP is the superior way. – cat Oct 7 '16 at 23:41
  • 3
    Does PHP have a foreach construct? You could do foreach ($i in range(1, count)) (whatever that looks like in PHP). Or something like foreach ($item in array.skip(1)) which is what a C# person would do. – usr Oct 8 '16 at 10:33
22

I hate both.

Who said you could use magic numbers? If you're going to start at an offset of 1 how about telling us WHY you're starting at an offset of 1. Adding an equally magic zero explains nothing to me.

Is this the payload offset? Is this some pascal string you're converting to a null terminated c string? Please tell us what is going on.

Sorry, but I've wasted much of my career decoding pointless mysteries like this and my patience for them has worn thin. Is a variable with a decent name really to much to ask?

By decent name I mean a name that explains WHY we're skipping the first element. Not something that simply says THAT we're skipping the first element. The 1 told me that on it's own.

  • 19
    0 and 1 are not magic numbers. – user949300 Oct 7 '16 at 18:59
  • 18
    @user949300 Oh they certainly are here. They are simply numbers that occasionally aren't magical. Just because 2 is always a magic number doesn't mean that 1 is never a magic number. The magic comes from missing meaning. What the heck does starting at a 1 offset mean here? What good does adding a 0 do here? Oh yes these numbers are certainly magical here. I can see the pixie dust dripping off of them. – candied_orange Oct 7 '16 at 19:03
  • 4
    Sure, I'll start defining static final int LONELIEST_NUMBER = 1 in all my Java code. :-) That said, on further thought I'd like to undownvote your answer but can't unless you edit it. Silly SO rule? – user949300 Oct 7 '16 at 19:46
  • 5
    @Eiko: If there is no explanation why the loop starts at the second element, it is virtually guaranteed that some maintenance programmer will change the loop to start at the first element. That is why the numeral 1 is a magic number in this context. – Bart van Ingen Schenau Oct 8 '16 at 7:18
  • 2
    @BartvanIngenSchenau I assumed there would be an immediately visible reason within the context. Like comparing elements with their predecessors (which I thought to be the most-frequent use case to start at 1 - and I fail to see any useful constant name here). If the first element has some ultra-special meaning, the design might be broken, and there should be better fixes than naming that index. I don't say there's never a reason to introduce a variable here. It's just that I think those cases are (or maybe only should be) very rare. – Eiko Oct 8 '16 at 7:30
12

Short answer: the first option is better.

The second option just adds noise. It is very unlikely that 0+1 helps the reader to understand that it could have been 0 but it is 1. Much more likely he will be puzzled a brief moment and distracted from what the loop is about. Especially in a language where all arrays start at 0.

As other mentioned, if you want to stress the fact that the loop starts from 1, not 0, just add a comment.

9

Don't tell us you're skipping the first item -- we can see that. What isn't obvious is why. So.. if it's not obvious from context, tell us why:

// array[0] is just a header
for($i=1 ; $i < count(array) ; $i++){
    array[$i];
}

Or, if you're comment-averse, something like:

$lastHeaderIndex = 0;
for($i = $lastHeaderIndex + 1 ; $i < count(array) ; $i++){
    array[$i];
}

Don't use comments and trickery to remind us how the language works.

6

Your example looks contrived. In real world code, the fact the loops needs to start at the second array element is most probably obvious from the following code lines. For example, if the real code looks like this

for($i=1 ; $i < count(array) ; $i++){
    array[$i-1]=array[$i];
}

there would be no explanation or "0+1" construct needed to make clear why the loop starts at 1 instead of 0.

However, if the code inside the loop does not explain the reasons in such an obvious way (maybe array[0] has a special meaning and must be dealt differently than the remaining elements), then add an explaining comment. But before you do this, think twice if you can avoid having array[0] this special meaning, and reorganize the surrounding code, which would probably be the better alternative.

  • In situations like your example code I like to name the variables "oneBasedIndex" or "zeroBasedIndex". Though something even more specific for that task would be better. – user949300 Oct 7 '16 at 23:22
5

Never seen option #2, but I like it. Why? With option #1 I'd wonder if the programmer forgot that arrays start at 0. Option #2 makes it clearer that they are deliberately starting at 1.

That said, best in either case to add a comment why you are skipping element.

Or, if you can easily describe why you are starting at one, use a constant. For example, if looking at command line arguments, something like

define ('FIRST_REAL_ARGUMENT', 1);
for ($i=FIRST_REAL_ARGUMENT; ...)

Personally, I'd probably just use a comment instead, YMMV.

  • I'll say that outside of environments where you're dealing with beginners, I've never had the issue of coworkers forgetting that arrays start at zero. I could see it happening if your work also used a language like MATLAB, but in most environments, I'd assume that the programmer knew what they were doing. And heck, starting a loop at 1 is pretty darn common. There's quite a few reasons to skip the first element of something. – Kat Oct 14 '16 at 20:00
2

I doubt anyone would be confused by the first one. We've all had to do it. So much so that the second one is much more likely to confuse. "Why is there a 0+ there? Did they override the + operator somehow?"

A decent compiler will turn the second one into the first anyway, but it looks like you're using PHP, which is interpreted. So every time the interpreter hits that loop, it's going to have to actually add 0 and 1. Not a big deal, but why make the interpreter do the work?

2

Use a variable that explain the start point.

You need to "skip the first element in a zero-based array", so for example:

skipFirstElement = 1;

for($i=$skipFirstElement ; $i < count(array) ; $i++){
    array[$i];
}
  • 5
    I like using a variable but I hate this name. It doesn't explain WHY you're skipping the first element. A 1 tells me that anyway. What good is a variable that needs to be renamed when it's value changes? I know the OP gave us no clue why so to pick a good name you're going to have to make up a reason. Without a better name I'd rather have the 1 back. – candied_orange Oct 11 '16 at 18:09
  • @CandiedOrange I agree with you that variable name should be more meaningful, but the problem as it is presented doesn't explain why he wants to skip first value. My point is, I just give an example, the author can choose a name that is best for him. – catta Oct 12 '16 at 5:58
  • Please don't teach this way. This is confusing enough. To many coders will reach for the easiest name they can so they can get back to writing hard to understand code. Seriously, I'd rather deal with the 1 than this. – candied_orange Oct 12 '16 at 6:03
1
//We are skipping the first element because...    
if ($i==0)  
{    continue;      } 

If one is obsessive with all loops starting at zero you could use a continue statement. Add a comment to why you are skipping since normally one wouldn't.

  • 1
    The problem with this option means that we're going to be evaluating this for every single element, adding n additional comparisons to the loop, while simply skipping the first one through some other method (shifting the array, starting with i = 1, whatever) means only doing the work you need to. – Kevin Fee Oct 7 '16 at 22:33
  • 3
    @KevinFee If you're not dealing with massive arrays, I'd say this simle comparison is not going to be an issue. And I quite like the explicitness of if first then skip with a comment saying why. Still without context none of the solutions are "best" – Ivan Pintar Oct 8 '16 at 6:46
-2

What I'd do is remove the first element before looping. Create a new array if you need to. Explain in a comment why you're doing it. And then just do a simple foreach.

$arrayCopy = $array; // in case you don't want to touch the original array
array_shift($arrayCopy); // removing first element because of X reason.
foreach($arrayCopy => $element) { 
    // do stuff
}

This way your intent is perfectly clear.

To clarify further you could wrap the code in a method with an appropriate name to make things clearer.

function doStuffToAllButTheFirst($array) { // this copies the original array, so there are no sideffects
    array_shift($array);
    foreach($array => $element) { // do stuff }  
}

However all of this is still missing context. What do you want to do with the elements? Will you be returning the new array? Do you care about the original and the new array after you doStuff()?

Anyway, there's no clear answer here, and deciding how to make the code readable depends greatly on the context.

  • 1
    I like this because I don't need to deal with indexes now, which is always a plus. – Tomas Zubiri Oct 7 '16 at 19:56
  • 1
    The disadvantage here is that if you don't want side effects, you need to copy the whole array. – Tomas Zubiri Oct 7 '16 at 19:59
  • 1
    And what if the reason we're starting at 1 is because we're doing $array[$i-1] = $array[$i] or something similar, as per @DocBrown's answer? – Kevin Fee Oct 7 '16 at 22:59
  • 1
    This seems horrible to me. Beside all the inefficiency, side effects and inability to solve quite common use cases for 1 (see Kevin Lee's comment), it doesn't make the code any clearer at all. The reader must understand array_shift, what it does, how it works. Maybe this line of code is a bug? Does it modifiy the array or return a new one? Does it insert an element or remove one? Does it change indices or not? I fail to see how using a one-based loop would not be a huge improvement in that function (and given its name, instantly understandable). – Eiko Oct 8 '16 at 7:17
  • 1
    The answers to these questions are highly dependent on the context. And as for efficiency, I'd take a foreach over for any day... Any performance difference is near to invisible in most cases. – Ivan Pintar Oct 8 '16 at 19:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.