Найти последний элемент массива при использовании цикла foreach в PHP


215

Я пишу создатель SQL-запроса, используя некоторые параметры. В Java очень просто обнаружить последний элемент массива из цикла for, просто проверяя текущую позицию массива с длиной массива.

for(int i=0; i< arr.length;i++){
     boolean isLastElem = i== (arr.length -1) ? true : false;        
}

В PHP они имеют нецелые индексы для доступа к массивам. Поэтому вы должны перебирать массив, используя цикл foreach. Это становится проблематичным, когда вам нужно принять какое-то решение (в моем случае добавить или / и параметр при построении запроса).

Я уверен, что должен быть какой-то стандартный способ сделать это.

Как вы решаете это в PHP?


2
Вы пытаетесь определить, должны ли вы соединять «И» или «ИЛИ» между частями предложения where?
Дэррил Хейн

1
просто указав, что вы должны хранить сумму в переменной, а не вызывать метод для каждой итерации. для (int i = 0, int t = arr.length; i <t; i ++).
OIS


Взгляните на это решение: stackoverflow.com/a/29474468/1478566
vbarbarosh

Ответы:


313

Похоже, вы хотите что-то вроде этого:

$numItems = count($arr);
$i = 0;
foreach($arr as $key=>$value) {
  if(++$i === $numItems) {
    echo "last index!";
  }
}    

При этом вы не должны перебирать «массив», используя foreachв php.


Я думаю, что я пойду на это решение, так как оно почти похоже на код, который я разместил. Даже ответ Джереми вполне подходит, но я думаю, что он немного сложнее, чем этот. Я не проводил никаких тестов, но я думаю, что этот ответ будет быстрее, поскольку он не извлекает массив ключей. Он будет иметь скорость O (1)
Vaibhav Kamble

18
$numItems = count($arr)хитрость не нужна и снижает читабельность - в PHP нет снижения производительности для доступа к счетчику ($ arr) каждый раз. Причина в том, что количество элементов внутренне сохраняется как специальное поле в заголовке массива и не рассчитывается на лету. Этот трюк происходит от других языков (C, Java?, ...).
johndodo

7
Интересно, @johndodo, что нет никакого снижения производительности за доступ к count ($ arr) каждый раз. Есть ли у вас какие-либо ссылки / источники, где эта конкретная оптимизация задокументирована? Спасибо!
Zuallauz

2
Очень печально, что в PHP наиболее правильное решение этой проблемы выглядит довольно не элегантно :(
kizzx2

1
@tomsihap $ i нужно увеличивать внутри цикла, в тандеме с итерацией массива. $ i должен представлять номер элемента в массиве, чтобы его можно было использовать для определения, когда был достигнут последний элемент. Без ++ цикл будет только когда-либо сравнивать «0» с общим количеством элементов.
Бобби Джек

201

Вы можете получить значение последнего ключа массива end(array_keys($array))и сравнить его с текущим ключом:

$last_key = end(array_keys($array));
foreach ($array as $key => $value) {
    if ($key == $last_key) {
        // last element
    } else {
        // not last element
    }
}

2
+1 Я согласен - другие решения опираются на массив, имеющий числовые индексы.
Патрик Гландиен

19
В свою защиту мой ответ не основан на том, что у массива есть цифровые клавиши :)
Ричард Левассер

2
Сравнение строк медленнее, чем целые числа, и не всегда точное при сравнении строк с целыми числами (по крайней мере, вы должны были использовать ===). Я голосую за это.
OIS

4
это элегантно, но вызывает STRICT NOTICE, потому что "end" ожидает эталонное значение :(
Wiliam

10
Исправлено для СТРОГО УВЕДОМЛЕНИЯ:$lastKey = array_search(end($array), $array);
Ajax

45

почему так сложно?

foreach($input as $key => $value) {
    $ret .= "$value";
    if (next($input)==true) $ret .= ",";
}

Это добавит, после каждого значения, кроме последнего!


2
Нет, если следующий вход $ содержит логическое значение false, что является серьезной проблемой для next ().
soulseekah

29
Если я не ошибаюсь, это не сработает, потому что вызов next () продвигает указатель массива, поэтому вы пропускаете все остальные элементы в цикле.
Джордан Лев

2
Кажется, не работает для меня. Второй последний элемент не получает запятую, но должен.
Зуаллауз

1
Если значение равно bool false, оно не работает. Также не печатает последнюю запятую между вторым, последним и последним значением.
OIS

1
Примечание для тех, кто хочет использовать это в PHP7 - указатель массива не перемещается в циклах foreach, и это не будет работать.
Скотт Флэк,

26

Когда toEnd достигает 0, это означает, что он находится на последней итерации цикла.

$toEnd = count($arr);
foreach($arr as $key=>$value) {
  if (0 === --$toEnd) {
    echo "last index! $value";
  }
}

Последнее значение все еще доступно после цикла, так что если вы просто хотите использовать его для большего количества вещей после цикла, это лучше:

foreach($arr as $key=>$value) {
  //something
}
echo "last index! $key => $value";

Если вы не хотите обрабатывать последнее значение как специальные внутренние циклы. Это должно быть быстрее, если у вас большие массивы. (Если вы повторно используете массив после цикла внутри той же области, вы должны сначала «скопировать» массив).

//If you use this in a large global code without namespaces or functions then you can copy the array like this:
//$array = $originalArrayName; //uncomment to copy an array you may use after this loop

//end($array); $lastKey = key($array); //uncomment if you use the keys
$lastValue = array_pop($array);

//do something special with the last value here before you process all the others?
echo "Last is $lastValue", "\n";

foreach ($array as $key => $value) {
    //do something with all values before the last value
    echo "All except last value: $value", "\n";
}

//do something special with the last value here after you process all the others?
echo "Last is $lastValue", "\n";

И чтобы ответить на ваш оригинальный вопрос "в моем случае добавить или / и параметр при построении запроса"; это зациклит все значения, затем соединит их вместе в строку с «и» между ними, но не перед первым значением или после последнего значения:

$params = [];
foreach ($array as $value) {
    $params[] = doSomething($value);
}
$parameters = implode(" and ", $params);

2
Конечно, он будет выполнять - $ toEnd для каждой итерации, вот и все. Если бы я переместил его за пределы цикла, он больше не работал бы.
OIS

Самый простой метод, который когда-либо использовался. $lastValue = array_pop($array);Спасибо.
Элиас Николас

21

Ответов уже много, но стоит посмотреть на итераторы, особенно потому, что об этом спрашивают стандартным способом:

$arr = range(1, 3);

$it = new CachingIterator(new ArrayIterator($arr));
foreach($it as $key => $value)
{
  if (!$it->hasNext()) echo 'Last:';
  echo $value, "\n";
}

Вы можете найти что-то более гибкое и для других случаев.


Отличный ответ. Я ценю, что вы используете функции языка, предназначенные для этой задачи. i=0;и ++i;всегда казался хакерским в языке сценариев, таких как PHP.
CheddarMonkey

15

Одним из способов может быть определение наличия итератора next. Если рядом с итератором нет следующего, это означает, что вы находитесь в последнем цикле.

foreach ($some_array as $element) {
    if(!next($some_array)) {
         // This is the last $element
    }
}

Я думаю, что это самый простой способ и требует наименьшего количества кода!
Привет, я zvaehn

1
Не работает с PHP 7+ как «В PHP 7 foreach не использует внутренний указатель массива».
Дэмиен Дебин

8

Итак, если ваш массив имеет уникальные значения массива, то определение последней итерации тривиально:

foreach($array as $element) {
    if ($element === end($array))
        echo 'LAST ELEMENT!';
}

Как видите, это работает, если последний элемент появляется в массиве только один раз, в противном случае вы получите ложную тревогу. В этом нет, вы должны сравнить ключи (которые являются уникальными наверняка).

foreach($array as $key => $element) {
    end($array);
    if ($key === key($array))
        echo 'LAST ELEMENT!';
}

Также обратите внимание на строгий оператор копаризации, который довольно важен в этом случае.


это довольно неэффективный способ.
Ваш здравый смысл

Нет. Это не. end () выполняет O (1). Он также короче, чем другие решения, и хорошо читается -> Если элемент равен концу массива, напишите «Last».
Рок Краль

Это более чем в два раза медленнее, чем мой первый и последний примеры для 100000 значений.
OIS

5

Предполагая, что у вас есть массив, хранящийся в переменной ...

foreach($array as $key=>$value) 
{ 
    echo $value;
    if($key != count($array)-1) { echo ", "; }
}

Это очень просто и полезно. Я только посчитал бы массив первым вне цикла foreach, чтобы программе не приходилось считать каждый раз, когда функция foreach оценивает каждый элемент.
Патрос

3
Это не будет работать на ассоциативных массивах. $ key не всегда число.
Джонатан

5

получить первый и последний элемент из массива foreach

foreach($array as $value) {
    if ($value === reset($array)) {
        echo 'FIRST ELEMENT!';
    }

    if ($value === end($array)) {
        echo 'LAST ITEM!';
    }
}

4

Вы все еще можете использовать этот метод с ассоциативными массивами:

$keys = array_keys($array);
for ($i = 0, $l = count($array); $i < $l; ++$i) {
    $key = $array[$i];
    $value = $array[$key];
    $isLastItem = ($i == ($l - 1));
    // do stuff
}

// or this way...

$i = 0;
$l = count($array);
foreach ($array as $key => $value) {
    $isLastItem = ($i == ($l - 1));
    // do stuff
    ++$i;
}

1
Пожалуйста, измените $ key = $ array [$ i]; $ key = $ keys [$ i]; в первом за цикл.
Нарек

4

Если вам нужно что-то сделать для каждого элемента, кроме первого или последнего, и только если в массиве более одного элемента, я предпочитаю следующее решение.

Я знаю, что есть много решений выше и опубликовано за месяцы / год до моего, но это то, что я считаю довольно элегантным само по себе. Проверка каждого цикла также является булевой проверкой, в отличие от числовой проверки «i = (count-1)», которая может позволить уменьшить накладные расходы.

Структура цикла может показаться неудобной, но вы можете сравнить ее с упорядочением тегов thead (начало), tfoot (конец), tbody (текущий) в тегах таблицы HTML.

$first = true;
foreach($array as $key => $value) {
    if ($first) {
        $first = false;
        // Do what you want to do before the first element
        echo "List of key, value pairs:\n";
    } else {
        // Do what you want to do at the end of every element
        // except the last, assuming the list has more than one element
        echo "\n";
    }
    // Do what you want to do for the current element
    echo $key . ' => ' . $value;
}

Например, в терминах веб-разработки, если вы хотите добавить border-bottom для каждого элемента, кроме последнего в неупорядоченном списке (ul), тогда вы можете вместо этого добавить border-top для каждого элемента, кроме первого (CSS: first-child, поддерживаемый IE7 + и Firefox / Webkit поддерживает эту логику, тогда как: last-child не поддерживается IE7).

Вы также можете свободно использовать переменную $ first для каждого вложенного цикла, и все будет работать отлично, поскольку каждый цикл делает $ first false во время первого процесса первой итерации (поэтому разрывы / исключения не вызовут проблем) ,

$first = true;
foreach($array as $key => $subArray) {
    if ($first) {
        $string = "List of key => value array pairs:\n";
        $first = false;
    } else {
        echo "\n";
    }

    $string .= $key . '=>(';
    $first = true;
    foreach($subArray as $key => $value) {
        if ($first) {
            $first = false;
        } else {
            $string .= ', ';
        }
        $string .= $key . '=>' . $value;
    }
    $string .= ')';
}
echo $string;

Пример вывода:

List of key => value array pairs:
key1=>(v1_key1=>v1_val1, v1_key2=>v1_val2)
key2=>(v2_key1=>v2_val1, v2_key2=>v2_val2, v2_key3=>v2_val3)
key3=>(v3_key1=>v3_val1)

Спасибо, это мое любимое решение! Это очень гибкий и стоит только логическое значение. Кстати, я думаю, что это будет работать для массива, содержащего хотя бы один элемент (не только более одного элемента).
х

4

Это должен быть простой способ найти последний элемент:

foreach ( $array as $key => $a ) {
    if ( end( array_keys( $array ) ) == $key ) {
        echo "Last element";
     } else {
        echo "Just another element";
     }
}  

Ссылка: Ссылка


- неработающая ссылка -
T30

3

У меня есть сильное чувство, что в корне этой "проблемы XY" ОП хотел просто implode()функционировать.


1
правда. Однако есть случаи, когда взрыв не так практичен. Представьте себе, например, попытку внедрить длинную строку html с большим количеством динамических переменных. Конечно, вы можете сделать для него ob_start / ob_get_clean или просто создать его как $ str = '...'. Но бывают случаи, когда это можно считать небольшим излишеством
Аластер Брайн

3

Поскольку ваше намерение найти массив EOF только для клея. Познакомьтесь с нижеприведенной тактикой. Вам не нужно требовать EOF:

$given_array = array('column1'=>'value1',
                     'column2'=>'value2',
                     'column3'=>'value3');

$glue = '';
foreach($given_array as $column_name=>$value){
    $where .= " $glue $column_name = $value"; //appending the glue
    $glue   = 'AND';
}
echo $where;

о / р:

column1 = value1 AND column2 = value2 AND column3 = value3


2

Похоже, вы хотите что-то вроде этого:

$array = array(
    'First',
    'Second',
    'Third',
    'Last'
);

foreach($array as $key => $value)
{
    if(end($array) === $value)
    {
       echo "last index!" . $value;
    }
}

2
Использование значения обычно не очень хорошая идея, потому что оно не будет работать должным образом, если массив имеет два одинаковых значения.
orrd

2

Не добавляйте запятую после последнего значения:

Массив:

$data = ['lorem', 'ipsum', 'dolor', 'sit', 'amet'];

Функция:

$result = "";
foreach($data as $value) {
    $resut .= (next($data)) ? "$value, " : $value;
}

Результат:

print $result;

лорем, ипсум, долор, сиди, амет


1

Вы можете сделать подсчет ().

for ($i=0;$i<count(arr);$i++){
    $i == count(arr)-1 ? true : false;
}

или если вы ищете ТОЛЬКО последний элемент, вы можете использовать end ().

end(arr);

возвращает только последний элемент.

и, как оказалось, вы можете индексировать php-массивы целыми числами. Это совершенно доволен

arr[1];

1
Недостаток в конце (arr) заключается в том, что он устанавливает внутренний указатель массива на последний элемент ..
Виджей

Нет, вы НЕ ДОЛЖНЫ использовать целые числа для доступа к массивам, если вы не знаете, что ключи являются числовыми и последовательными. Рассмотрим: $a = array(0=>'A', 2=>'B', 'aaa'=>'C'). Что вы получите, если получите доступ $a[count($a)-1]?
johndodo

1

Вы также можете сделать что-то вроде этого:

end( $elements );
$endKey = key($elements);
foreach ($elements as $key => $value)
{
     if ($key == $endKey) // -- this is the last item
     {
          // do something
     }

     // more code
}

end возвращает значение, а не массив, поэтому способ, которым вы его сделали, не работает. Сравнение строк также медленнее, чем целое число.
OIS

Ты прав. должно быть конец ($ elements); $ endKey = key ($ elements);
KOGI

1

Мне нравится следующее, поскольку я чувствую, что это довольно опрятно. Давайте предположим, что мы создаем строку с разделителями между всеми элементами: например, a, b, c

$first = true;
foreach ( $items as $item ) {
    $str = ($first)?$first=false:", ".$item;
}

сделать это более простым, не объявляя $ first; использовать foreach ($ items как $ key => $ item), а затем $ str = ($ key == 0)?
Милан Rilex Ristic


0

Вот еще один способ сделать это:

$arr = range(1, 10);

$end = end($arr);
reset($arr);

while( list($k, $v) = each($arr) )
{
    if( $n == $end )
    {
        echo 'last!';
    }
    else
    {
        echo sprintf('%s ', $v);
    }
}

0

Если я вас понимаю, то все, что вам нужно, это обратить массив и получить последний элемент командой pop:

   $rev_array = array_reverse($array);

   echo array_pop($rev_array);

0

Вы также можете попробовать это сделать ваш запрос ... показанный здесь с INSERT

<?php
 $week=array('one'=>'monday','two'=>'tuesday','three'=>'wednesday','four'=>'thursday','five'=>'friday','six'=>'saturday','seven'=>'sunday');
 $keys = array_keys($week);
 $string = "INSERT INTO my_table ('";
 $string .= implode("','", $keys);
 $string .= "') VALUES ('";
 $string .= implode("','", $week);
 $string .= "');";
 echo $string;
?>

0

Для сценариев, генерирующих SQL-запросы, или для чего-либо, что выполняет другое действие для первого или последнего элемента, гораздо быстрее (почти в два раза быстрее) избежать ненужных проверок переменных.

Текущее принятое решение использует цикл и проверку внутри цикла, которая будет выполняться every_single_iteration, правильный (быстрый) способ сделать это заключается в следующем:

$numItems = count($arr);
$i=0;
$firstitem=$arr[0];
$i++;
while($i<$numItems-1){
    $some_item=$arr[$i];
    $i++;
}
$last_item=$arr[$i];
$i++;

Небольшой домашний тест показал следующее:

test1: 100000 прогонов модели Морг

время: 1869.3430423737 миллисекунд

test2: 100000 прогонов модели, если последний

время: 3235.6359958649 миллисекунд


0

Другой способ - запомнить предыдущий результат цикла цикла и использовать его в качестве конечного результата:

    $result = $where = "";
    foreach ($conditions as $col => $val) {
        $result = $where .= $this->getAdapter()->quoteInto($col.' = ?', $val);
        $where .=  " AND ";
    }
    return $this->delete($result);

0

Я лично использую такую ​​конструкцию, которая позволяет легко использовать элементы html <ul> и <li>: просто изменить равенство для другого свойства ...

Массив не может содержать ложные элементы, но все остальные элементы, которые приводятся к ложному логическому значению.

$table = array( 'a' , 'b', 'c');
$it = reset($table);
while( $it !== false ) {
    echo 'all loops';echo $it;
    $nextIt = next($table);
    if ($nextIt === false || $nextIt === $it) {
            echo 'last loop or two identical items';
    }
    $it = $nextIt;
}


0
<?php foreach($have_comments as $key => $page_comment): ?>
    <?php echo $page_comment;?>
    <?php if($key+1<count($have_comments)): ?> 
        <?php echo ', '; ?>
    <?php endif;?>
<?php endforeach;?>

0

Вот мое решение: просто получите счетчик вашего массива, минус 1 (так как они начинаются с 0).

$lastkey = count($array) - 1;
foreach($array as $k=>$a){
    if($k==$lastkey){
        /*do something*/
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.