on a RECENT SP thread I read the following in regards to adding a class to last OR first item on a list.
… I’d put the image at the BOTTOM and have the class on the last one, but that’s usually because in my PHP it’s easier to detect last than first.
I am really curious to learn how it would be easier to detect the last item of a dataset in PHP. Normally I would have taken the opposite approach, on the assumption that all list will have at least one item
<?
echo"<ul>\
\ \ <li class="first">";
// the loop woud go here adding "</li>\
\ \ <li>$data" after each iteration;
echo</li>\
\ </ul>"
?>
of course that assumes there is at least one item to be output. an alternate method I like to use if a hold over from my JHS BASIC days, which is to use a $flag technique…
<?
$flag="class='myclass'
// foreach or while loop {
echo "\ \ <li $flag>$data</li>\
";
$flag="";
//
both this work great for targeting the first item… but make it near impossible to target the last item. I would love to know the “easy way” to know when the LAST ITEM in a data set is being iterated ( in other words when at he last item is about to go through the loop so it can be marked as such)
@AnthonySterling: looks good, but it doesn’t show why detecting the last is ~easier~ than detecting the first
@dresden_phoenix: I have no clue why finding the last would be easier/faster would be easier than finding the first (IMO it’s the other way around). Do you remember who said that, and in what context?
could also be done as a SWITCH if you had more than just first/last to find.
(Note: This applies only to numerical arrays. Anthony’s solution (I believe, correct me if i’m wrong here, Anthony) works on either type of array, though may have unexpected results if you use a mixed array. (Mixed arrays always fark things up.)
In the cases I need to target the last item, I’m also usually marking the 3rd or 4th (or whatever) iteration of an array so I have some sort of counter anyways.
In that case I just: if($i == (count($array) - 1) // do whatever
This is not entirely on topic, but if you need the number of elements in an array in a loop, and the loop does not modify the array, it’s better to get the count before the loop and use that, instead of letting PHP count the number of elements in the array every single iteration.
i.e. don’t do
for ($i=1; $i<$something; $i++) {
if (count($array) == $somethingElse) {
// ...
}
}
but instead
$count=count($array);
for ($i=1; $i<$something; $i++) {
if ($count == $somethingElse) {
// ...
}
}
Ah, the joys of array iteration, since that’s my comment being quoted, perhaps I should be the one to explain? That was a ‘my bad’ statement as it’s more just a matter of how I build things than any actual advantage. Let’s just say I shouldn’t post while I’m still waking up…
That said I’m kind-of laughing at the solutions presented so far given how simple array manipulation is in php.
Pulls first key, last key, and resets the pointer to the top of the array. (though a foreach will do the reset anyways) – My typical output code being:
foreach ($myArray as $key => $data) {
$classList=array();
if ($key=$firstKey) $classList[]='first';
if ($key=$lastKey) $classList[]='last';
if ($key=$currentItemKey) $classList[]='current';
if ($data['offside']) $classList[]='offSide';
// need more checks for classes, put them here
echo '<li',(
count($classList)==0 ? '' : ' class="'.implode(' ',$classList).'"'
).'>'
',$data['name'],'
</li>';
}
… and it doesn’t require an extra counter variable to be incremented inside the loop. The above extra logic and array tricks meaning no extra spaces, no saying class when you don’t need it, and if there’s only one element it gets both the first and last class… a problem with the oop version up above. (which reeks of objects for nothing much less the overhead of all the function/method calls).
So my bad on the “bottom is easier” statement – it’s not really any more or less work to pull either end… More force of habit I guess…
– edit –
Oh, and if you need to modify the data, you do NOT have to use a for loop… as the reference modifier works with foreach. (PHP5+)
foreach ($myArray as $key => &$data) {
$data['name'].='_heyItWorks';
}
I’m amazed how many people still use for with a variable to handle that… especially when you can’t trust numeric keys in php arrays.
Very true, right now that is. Of course, once you start need this behaviour in different parts of your application, add/remove additional behaviour in only specific areas or use this in another application you’ll be glad you had something a little more portable/extensible.
There’s many ways to skin a cat, I just know which way I prefer.
$beatles = array ('John', 'Paul', 'George', 'Ringo');
$first = array_shift($beatles);
$last = array_pop($beatles);
echo "<li>First is $first </li>";
foreach( $beatles as $beatle)
echo "<li>$beatle</li>";
echo "<li>Last is $last</li>";
// First is John
// Paul
// George
// Last is Ringo
'Though you’d need to rebuild the array to use it elsewhere on the same page unless you do:
It’s funny, but I always hear the “lets throw an object at it for NOTHING” thing defended with those two wonderful words – portable and extensible… Having cut my teeth on objects in Smalltalk and Modula, and understanding when objects have the advantage and when they don’t… I really wonder just where the devil those ALLEGED advantages would even be relevant, ESPECIALLY when talking user objects in an INTERPRETED language.
There is no way in HELL that is a job for a five method userland object that puts static’s on the stack! Much less the memory overhead of copying the entire array onto the heap just to find the first and last key.
But then, I consider ArrayIterator needlessly complex, convoluted and a pointless waste of code for such a simple variable construct as an array… I think that stems from all that machine language I’ve done where the rule of thumbs are “far calls bad, duplicate of large memory object on the heap just to throw it out two lines later bad” etc, etc… That half it’s methods are “currently not documented” – mostly the methods that actually look useful… Throws up warning flags to say away from it.
That and it seems to violate the most simple of programming concepts, the less code you use, the less there is to break.
I don’t even want to THINK about the memory thrash and garbage collection nightmare THESE:
Would create if you called them to check on every iteration. Optimize inside the loop man, inside the loop.
On that note, I’m currently working with something using GD, and I feel the need to *****-slap whoever wrote the library tie-ins on that front; I’ve never spent so much time manually doing my own garbage collection in PHP… I’ve got “unset” on damned near every other line just trying to keep the bloody thing from sucking down 10 megs of RAM just to process a single 1 megapixel image… If I wasn’t calling unset every other line I’d end up with 20 copies in memory at once.
But then, most PHP programmers are blissfully unaware of the underlying basics of how things work – and their code suffers for it.
That said, if I were to try and use objects for this, I’d probably wrap __construct instead and pull first/last’s keys when initializing, and then also wrap “append” and the various sort functions to trap when/if new ones are added to recalculate them. That way inside your iterations you aren’t creating a copy and deallocating it off the heap every time you check for first and last… you’d only have to create copies when doing an append or sort – routines called a lot less frequently.
Something like:
class firstLastArrayIterator extends ArrayIterator {
public $firstKey=null,$lastKey=null;
public function __construct($array) {
/*
do these here instead of calling updateKeys method
1) $this->getArrayCopy will fail before parent constructor is called
2) we don't need to make a copy when we can operate on the parameter.
*/
reset($array);
$this->firstKey=key($array);
end($array);
$this->lastKey=key($array);
return parent::__construct($array);
}
private function updateKeys() {
$array=$this->getArrayCopy();
/* new array is always reset */
$this->firstKey=key($array);
end($array);
$this->lastKey=key($array);
}
public function append($value) {
parent::append($value);
updateKeys();
}
public function asort() {
parent::asort();
updateKeys();
}
public function ksort() {
parent::ksort();
updateKeys();
}
public function natsort() {
parent::natsort();
updateKeys();
}
public function natcasesort() {
parent::natcasesort();
updateKeys();
}
public function uasort($cmp_function) {
parent::uasort($cmp_function);
updateKeys();
}
public function uksort($cmp_function) {
parent::uksort($cmp_function);
updateKeys();
}
public function isfirst() {
return $this->key()==$this->firstKey;
}
public function islast() {
return $this->key()==$this->lastKey;
}
}
WAY too much code for my tastes, but if you were to use objects for this it seems the more… sane approach to the problem… and would probably run ten to fifteen times faster. Hell, I’d even consider taking an axe to the isfirst and islast calls – but I’ve still got that old school “function calls for single operations BAD” thing drilled into my brain.
I still think "ok, push state to the stack, push parameters to the stack, far call, allocate local vars on the heap… then on exit deallocate heap, ret, pop state off the stack… "
Hell of a lot of overhead for a simple conditional. Part of why I think KEY should have been a property and not a method on the iterators. It’s a property that should have been set when the object is initialized and/or next is called.
But given that objects are shoe-horned into PHP worse than they are in C++, inefficiencies like that are hardly surprising.
An interesting approach – not wild about having the echo statements separate (maybe put them in a function? The call overhead would be bad though) or the increased memory fragmentation – but I do like the LACK of if statements inside the loop – after all the cornerstone of good optimization is to concentrate on inside the loop… The big problems I see is if you can’t predict the data set size and you give it a single element array… you’ll get the first element and then either blanks/empty LI or possibly PHP errors… when the ideal version should output both “first” and “last” as positive if you feed it a one element array.
I’m certainly to blame for not optimising it as much as I should have, and I hate to use the “It only took x minute(s)” excuse too. I was aware of the memory implications and probably should have addressed them prior to offering up one of many solutions to this problem.
I’m going to disagree on the having a method do more than one thing, it seems to fly in the face of nearly everything I heard/read from other seasoned developers. I also don’t like your offering, surely there’s “more” to go wrong in that implementation, and to quote yourself:
Thanks for your post, barring a couple of points, I agreed with most of it.
<?php
$i = 0;
$blah = array('red','yellow','blue','purple');
$total = count($blah);
foreach ($blah as $var) {
$i++;
switch {
case 1:
echo '<div class="first">' . $var . '</div>';
break;
case $total:
echo '<div class="last">' . $var . '</div>';
break
default:
echo 'This is not the first or the last so I am skipping it!';
}
}
?>
Or even…
<?php
$colors = array('red','yellow','blue','purple');
$total = count($colors)-1; // minus one, arrays start with zero and not one.
$first = echo '<div class="first">' . $blah[0] . '</div>';
$last = echo '<div class="last">' . $blah[$total] . '</div>';
?>
this is NOT a javascript discussion, but a PHP one.
this type of data processing has no business being done client side, at least not using a user side scripting technology that may or may not be available.
throwing a fat bloated steaming pile of manure scripting library at it is NOT the answer – EVER.
I’m saying that if a method only does one thing, it shouldn’t be a method… I’m not sure what you heard from “seasoned developers” but wasting a function or method on checking a value that should be updated ONLY when the properties of the class change… or simply creating functions that only do one operation is a colossal waste of code and the antithesis of good programming. It is the programming equivalent of slapping DIV’s and classes around everything for no good reason!
Programming 101, a function call has ten to a hundred times the overhead and processing time of a variable reference – It’s why in compiled languages a function call like this one:
public function isfirst() {
return $this->key()==$this->firstKey;
}
if ($myArray->isFirst()) {
can be as much as 100 times slower than just doing
if ($myArray()->key()==$myArray->firstKey) {
… and using a method for key being as much as 100 times slower than storing the current key as a property of the object iterator – which is why in an ideal programming environment “current” should be null at reset, and next() should return a pointer (reference) to the record instead of a copy of it.
… and while PHP is an interpreted language not a compiled one, for the most part the above still holds true and can in fact be many times WORSE. It’s why userland objects and PHP variables have ridiculous amounts of overhead. (to go with the lack of strict typecasting pissing all over any chances of optimized operations on data)
I’m not wild about it either, but it lacks the performance issues yours would have and is a bit more bulletproof.
don’t see how – most of the code is simple copypasta not true functionality to just wrap any function that could change what the first and last keys in the array are… that’s one of the whole reasons for the difference between methods and properties; whenever possible frequently referenced but rarely changed values should be set as properties instead of getting the overhead of a method involved. You throw a 30 record array at it, you might do one append, maybe one sort… but you’ll do 30 iterations of checking against first and last if you need to check both states in a manner that doesn’t break when there’s only one record.
Most data processing needing to recognize zero, 1 or an infinite number of values. Something the code other people are presenting here seem to keep missing.
@LinuxFreelancer – like with yours. Works fine when you know there are more than two of them, but if you don’t know the array set… pass yours an empty array or an array with only one value; what’s it do? NOT a good candidate for switch because depending on the data set BOTH conditions could be true – SWITCH/CASE is only good when the results are AND OR… in this case the handler needs to trap OR OR, running BOTH the first and last test. It’s the same reason Cups version doesn’t quite pass muster.
Notice that in the output side of mine, I’m checking “if {} if {}”, not “if {} elseif {}”… if there’s one record, it’s both first AND last. When pulling your results from a database, possibly with LIMIT… You cannot/should not make assumptions on the size of the result array.
Also not wild about incrementing the extra variable when you could just be pulling the meaningful bits from the array itself… but that approach would be handy in those cases where you also need to label them “odd” or “even”.