Как правильно увеличить некоторый ключ массива, даже если ключ необходимо создать?


Предположим, вам нужно создать какую-то "вершину" и иметь такой код:

$matches=array();
foreach ($array as $v){
   $matches[processing($v)]++;  
}

Это выведет Notice: Undefined index для случаев, когда необходимо создать индекс.

Каков был бы наилучший способ решения этих проблем, поскольку вы ЗНАЕТЕ, что вам придется создавать индексы?

Я использовал эти решения в зависимости от случая:

  1. Подавление ошибки @$matches[$v]++;
    Pro: ОЧЕНЬ легко вводить
    Кон: медленно
  2. Проверка, установлен ли он $matches[$v]=isset($matches[$v])?$matches[$v]++:1;
    Pro: быстрее
    Con: требуется больше времени для записи даже в сокращенной форме, и необходимо использовать $matches[$v] еще 2 раза

Есть ли другие способы?
Ищу самое быстрое время выполнения, так как я использую эту функцию тысячи раз или какой-нибудь более ленивый способ ввода , который все равно быстрее, чем @

РЕДАКТИРОВАТЬ:

В простом случае, когда у вас есть $matches[$v]++;, вы также можете использовать array_count_values() ( как Йоши предложенный)

Author: CSᵠ, 2013-01-10

3 answers

После некоторого чтения, написания и тестирования я получил кое-что:

function inc(&$var){
    if (isset($var)) $var++;else $var=1;
}

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

Тестовый код:

$a=array();

// Pre-Fill array code goes here
for($i=1;$i<100000;$i++) {
    $r=rand(1,30000);
    //increment code goes here
}

// Remove extra keys from array with:
//foreach ($a as $k=>$v) if ($v==0) unset($a[$k]);

Время выполнения: (только в информационных целях)

inc($a[$r])                             1.15-1.24
@$a[$r]++                                   1.03-1.09
$a[$r]=array_key_exists($r,$a)?$a[$r]++:1;  0.99-1.04

$a[$r]=!empty($a[$r])?$a[$r]++:1;               0.61-0.74
if (!empty($a[$r])) $a[$r]++;else $a[$r]=1; 0.59-0.67
$a[$r]=isset($a[$r])?$a[$r]++:1;                0.57-0.65
if (isset($a[$r])) $a[$r]++;else $a[$r]=1;  0.56-0.64


//with pre-fill
$a=array_fill(0,30000,0);                   +0.07(avg)
for($i=1;$i<=30000;$a[$i++]=0);             -0.04(avg)

//with pre-fill and unset
$a=array_fill(0,45000,0);                   +0.16(avg)
for($i=1;$i<=45000;$a[$i++]=0);             +0.02(avg)

Выводы:

  • @, конечно, самый быстрый для ввода, и я не вижу никаких проблем в его использовании в данном случае, но не стесняйтесь также проверять этот вопрос: Подавлять ошибку с помощью оператора @ в PHP
  • полное подавление ошибок (до цикла и включение ошибок после цикла) с помощью ini_set() хуже всего сказывается на производительности
  • inc() выглядит красиво и чисто, легко набирается и выполняет проверку вместо подавления, но вызов выглядит еще медленнее, чем @
  • isset() немного быстрее, чем empty(), но оба работают примерно одинаково
  • интересно использовать стенографию if операторы немного медленнее!
  • лучший результаты, достигнутые при предварительном заполнении массива. Даже если длина неизвестна, хороший прогноз все равно будет немного быстрее для огромного набора данных
  • как ни странно, array_fill() занимает немного больше времени, чем for ?!?!

RFC

Я не считаю этот ответ на 100% полным, хотя на данный момент он выглядит как isset() самый быстрый и @ самый ленивый.
Любые комментарии и идеи приветствуются!

 6
Author: CSᵠ, 2017-05-23 12:17:02

Возможно, вы могли бы инициализировать массив совпадений для начала, используя array_combine для объединения значений из $array в качестве ключей и array_fill для заполнения значений начальным 0

$matches = array_combine(array_values($array), array_fill(0, count($array), 0));
 1
Author: Crisp, 2013-01-10 15:29:40

Я всегда делаю:

$matches=array();

foreach ($matches as $v){

    /* if $v is not empty, add 1, otherwise $v= 1 */
    $matches[$v]=(!(empty($matches[$v]))) ? $matches[$v]++ : 1;
}

Ты прав. Это немного многословно, но, честно говоря, в то же время довольно лаконично. Хотя я использую empty() вместо isset(). Не уверен, что это быстрее или медленнее у меня в голове. Я думаю, что это, вероятно, медленнее.

Редактировать

Чтобы ответить на ваше редактирование, я бы сделал это так:

$matches=array();

foreach ($matches as $v){

    $x=function($v);

    /* if $v is not empty, add 1, otherwise $v= 1 */
    $matches[$x]=(!(empty($matches[$x]))) ? $matches[$x]++ : 1;
}

Таким образом, вы вызываете функцию только один раз.

 0
Author: Marc, 2013-01-10 15:27:12