Как проверить, существует ли несколько ключей массива

87

У меня есть множество массивов, которые будут содержать

story & message

или просто

story

Как мне проверить, содержит ли массив и историю, и сообщение? array_key_exists()ищет только этот единственный ключ в массиве.

Есть ли способ сделать это?

Райан
источник
2
Если "история" будет там в любом случае, похоже, вам действительно просто нужно проверить наличие "сообщения".
Wyzard 01
5
Используя array_intersect_key()сравните массив ключей, которые вы хотите проверить, с массивом, который вы проверяете. Если длина вывода такая же, как у массива ключей для проверки, они все присутствуют.
Майкл Берковски
Wyzard, у меня есть другие массивы, которые содержат сообщение, но не историю, но у них есть другие ключи, которые может содержать только массив с историей или историей и сообщением. Спасибо
Райан
Вы путаете здесь ключи и значения? Форматирован ли массив как ["story & message" => "value"]или это больше похоже["story & message"]
GordonM

Ответы:

69

Если у вас есть только 2 ключа для проверки (как в исходном вопросе), вероятно, достаточно просто array_key_exists()дважды позвонить, чтобы проверить, существуют ли ключи.

if (array_key_exists("story", $arr) && array_key_exists("message", $arr)) {
    // Both keys exist.
}

Однако это явно не подходит для многих ключей. В этой ситуации поможет настраиваемая функция.

function array_keys_exists(array $keys, array $arr) {
   return !array_diff_key(array_flip($keys), $arr);
}
Алекс
источник
3
Если люди думают, что другие решения лучше подходят для проверки наличия двух членов в массиве, им не должен нравиться четкий читаемый код или производительность :)
Алекс
Это, вероятно, самое простое решение, если требуемых ключей относительно мало. Если они станут нечитаемыми, если они будут примерно 20 или 30.
apokryfos
1
@apokryfos Согласен, но он отвечает на вопрос ОП.
Alex
2
@alex единственная проблема в том, что если он $keysсодержит один элемент, которого нет,$arr а другой находится в нем, !array_diff_keyвозвращает empty => false( пример 3v4l ) ...
CPHPython
3
Я думаю, что это можно сделать более читаемым с помощью использования, !array_diff($keys, array_keys($array));потому что при их разработке требуется немного меньше когнитивной нагрузки array_flip.
moopet
193

Вот решение, которое можно масштабировать, даже если вы хотите проверить наличие большого количества ключей:

<?php

// The values in this arrays contains the names of the indexes (keys) 
// that should exist in the data array
$required = array('key1', 'key2', 'key3');

$data = array(
    'key1' => 10,
    'key2' => 20,
    'key3' => 30,
    'key4' => 40,
);

if (count(array_intersect_key(array_flip($required), $data)) === count($required)) {
    // All required keys exist!
}
Эрфан
источник
Я хотел бы знать причину, по которой это было отклонено ... ага, это быстрее, потому что array_intersect_key реализован на C, и вам не понадобится цикл
Эрфан
На самом деле, довольно умно, хорошо сделано, хотя и трудновато для чтения.
Jon z
Спасибо :) Странно, что PHP не имеет встроенной функции для этого - это довольно распространенное явление. Существует множество классов проверки пользовательского ввода, которые делают это, но для большинства случаев использования это излишнее,
Эрфан
12
Действительно умное решение, но оно действительно медленнее (примерно на 50% медленнее на моем компьютере), чем простое: `` $ ok = true; foreach ($ требуется как $ field) {if (! array_key_exists ($ field, $ data)) $ ok = false; }
Ож
@Ozh, кроме того, что array_key_exists работает медленнее, чем isset
iautomation 01
34

Удивительно array_keys_existне существует ?! Тем временем это оставляет место для определения однострочного выражения для этой общей задачи. Я думаю о сценарии оболочки или другой небольшой программе.

Примечание: каждое из следующих решений использует краткий […]синтаксис объявления массива, доступный в php 5.4+

array_diff + array_keys

if (0 === count(array_diff(['story', 'message', '…'], array_keys($source)))) {
  // all keys found
} else {
  // not all
}

(подсказка Ким Стакс )

Это самый краткий подход, который я нашел. array_diff()возвращает массив элементов, присутствующих в аргументе 1, отсутствующих в аргументе 2. Следовательно, пустой массив указывает, что все ключи были найдены. В php 5.5 вы можете упростить, 0 === count(…)чтобы быть просто empty(…).

array_reduce + unset

if (0 === count(array_reduce(array_keys($source), 
    function($in, $key){ unset($in[array_search($key, $in)]); return $in; }, 
    ['story', 'message', '…'])))
{
  // all keys found
} else {
  // not all
}

Сложнее читать, легко изменить. array_reduce()использует обратный вызов для перебора массива для получения значения. Подавая ключи мы заинтересованы в $initialстоимости , $inа затем удаление ключей найдены в источнике можно ожидать до конца с 0 элементами , если были найдены все ключи.

Конструкцию легко изменить, так как интересующие нас клавиши хорошо вписываются в нижнюю строку.

array_filter & in_array

if (2 === count(array_filter(array_keys($source), function($key) { 
        return in_array($key, ['story', 'message']); }
    )))
{
  // all keys found
} else {
  // not all
}

Проще написать, чем array_reduceрешение, но немного сложнее отредактировать. array_filterтакже является итеративным обратным вызовом, который позволяет создавать фильтрованный массив, возвращая в обратном вызове значение true (копировать элемент в новый массив) или false (не копировать). Проблема в том, что вы должны изменить 2количество предметов, которое вы ожидаете.

Это можно сделать более надежным, но граничащее с абсурдной читабельностью:

$find = ['story', 'message'];
if (count($find) === count(array_filter(array_keys($source), function($key) use ($find) { return in_array($key, $find); })))
{
  // all keys found
} else {
  // not all
}
Марк Фокс
источник
3
для небольших наборов разница будет незначительной. если вы пишете библиотеку / платформу, которая обрабатывает большие наборы данных, вам, вероятно, следует протестировать производительность каждого модуля, чтобы найти узкие места, а не проводить преждевременную оптимизацию.
Марк Фокс
16

Мне кажется, что самый простой способ был бы таким:

$required = array('a','b','c','d');

$values = array(
    'a' => '1',
    'b' => '2'
);

$missing = array_diff_key(array_flip($required), $values);

Печать:

Array(
    [c] => 2
    [d] => 3
)

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

Петр Цибулка
источник
Вот зачем я сюда приехал!
eNeMetcH
8

Еще одно возможное решение:

if (!array_diff(['story', 'message'], array_keys($array))) {
    // OK: all the keys are in $array
} else {
   // FAIL: some keys are not
}
Василий
источник
7

Вышеупомянутые решения умны, но очень медленны. Простой цикл foreach с isset более чем в два раза быстрее array_intersect_keyрешения.

function array_keys_exist($keys, $array){
    foreach($keys as $key){
        if(!array_key_exists($key, $array))return false;
    }
    return true;
}

(344 мс против 768 мс для 1000000 итераций)

автоматизация
источник
isset вернет false, если ['key' => null] и иногда у вас есть массивы с нулевыми значениями. Вы должны использовать array_key_exists вместо isset
j4r3k
Мне пришлось использовать противоположное здесь из-за преждевременного возврата с false( в данном случае falseпереопределения true). Итак, что подходит для моих нужд, так это то, что foreach ($keys as $key) { if (array_key_exists($key, $array)) { return true; }} return false;мне нужно было, если бы anyключ в массиве существовал в другом массиве ...
Джефф,
1
Я бы не назвал +/- 400 мс на миллион клавиш "очень медленными", но я всего лишь человек!
полковник клик
3

Если у вас что-то вроде этого:

$stuff = array();
$stuff[0] = array('story' => 'A story', 'message' => 'in a bottle');
$stuff[1] = array('story' => 'Foo');

Вы могли просто count():

foreach ($stuff as $value) {
  if (count($value) == 2) {
    // story and message
  } else {
    // only story
  }
}

Это работает только в том случае, если вы точно знаете, что у вас есть ТОЛЬКО эти ключи массива и ничего больше.

Использование array_key_exists () поддерживает проверку только одного ключа за раз, поэтому вам нужно будет проверять оба по отдельности:

foreach ($stuff as $value) {
  if (array_key_exists('story', $value) && array_key_exists('message', $value) {
    // story and message
  } else {
    // either one or both keys missing
  }
}

array_key_exists()возвращает истину, если ключ присутствует в массиве, но это настоящая функция, и ее нужно много набирать. Конструкция языка isset()будет делать почти то же самое, за исключением случая, когда проверяемое значение равно NULL:

foreach ($stuff as $value) {
  if (isset($value['story']) && isset($value['message']) {
    // story and message
  } else {
    // either one or both keys missing
  }
}

Дополнительно isset позволяет проверять сразу несколько переменных:

foreach ($stuff as $value) {
  if (isset($value['story'], $value['message']) {
    // story and message
  } else {
    // either one or both keys missing
  }
}

Теперь, чтобы оптимизировать тест для заданного материала, вам лучше использовать это «если»:

foreach ($stuff as $value) {
  if (isset($value['story']) {
    if (isset($value['message']) {
      // story and message
    } else {
      // only story
    }
  } else {
    // No story - but message not checked
  }
}
Свен
источник
3

Как насчет этого:

isset($arr['key1'], $arr['key2']) 

только вернуть истину, если оба не равны нулю

если ноль, ключ отсутствует в массиве

Давид Дутковский
источник
1
если значение $arr['key1']или $arr['key2']равно null, код будет, ключ все еще существует.
Xorifelse 04
Я написал тест, посмотрите его @Xorifelse test и поправьте меня, если я ошибаюсь. К вашему сведению: в то время я знал только версию PHP 5.6. *, Поэтому я знал только ее.
Давид Дутковский
Что пытается достичь этот код? Почему вы просто не пользуетесь foreachпетлей?
Xorifelse 08
Я хотел добавить доказательство того, что issetфункция работает так, как я имел в виду, но теперь я понимаю, что вы были правы, ключи все еще остаются в массиве, и поэтому мой ответ неверен, спасибо за отзыв. Да, я мог бы это использовать foreach.
Давид Дутковский
3

Я использую что-то подобное довольно часто

$wantedKeys = ['story', 'message'];
$hasWantedKeys = count(array_intersect(array_keys($source), $wantedKeys)) > 0

или найти значения для желаемых ключей

$wantedValues = array_intersect_key($source, array_fill_keys($wantedKeys, 1))
Стив
источник
2

попробуй это

$required=['a','b'];$data=['a'=>1,'b'=>2];
if(count(array_intersect($required,array_keys($data))>0){
    //a key or all keys in required exist in data
 }else{
    //no keys found
  }
Валмаяки
источник
1

Это функция, которую я написал для себя, чтобы использовать ее в классе.

<?php
/**
 * Check the keys of an array against a list of values. Returns true if all values in the list
 is not in the array as a key. Returns false otherwise.
 *
 * @param $array Associative array with keys and values
 * @param $mustHaveKeys Array whose values contain the keys that MUST exist in $array
 * @param &$missingKeys Array. Pass by reference. An array of the missing keys in $array as string values.
 * @return Boolean. Return true only if all the values in $mustHaveKeys appear in $array as keys.
 */
    function checkIfKeysExist($array, $mustHaveKeys, &$missingKeys = array()) {
        // extract the keys of $array as an array
        $keys = array_keys($array);
        // ensure the keys we look for are unique
        $mustHaveKeys = array_unique($mustHaveKeys);
        // $missingKeys = $mustHaveKeys - $keys
        // we expect $missingKeys to be empty if all goes well
        $missingKeys = array_diff($mustHaveKeys, $keys);
        return empty($missingKeys);
    }


$arrayHasStoryAsKey = array('story' => 'some value', 'some other key' => 'some other value');
$arrayHasMessageAsKey = array('message' => 'some value', 'some other key' => 'some other value');
$arrayHasStoryMessageAsKey = array('story' => 'some value', 'message' => 'some value','some other key' => 'some other value');
$arrayHasNone = array('xxx' => 'some value', 'some other key' => 'some other value');

$keys = array('story', 'message');
if (checkIfKeysExist($arrayHasStoryAsKey, $keys)) { // return false
    echo "arrayHasStoryAsKey has all the keys<br />";
} else {
    echo "arrayHasStoryAsKey does NOT have all the keys<br />";
}

if (checkIfKeysExist($arrayHasMessageAsKey, $keys)) { // return false
    echo "arrayHasMessageAsKey has all the keys<br />";
} else {
    echo "arrayHasMessageAsKey does NOT have all the keys<br />";
}

if (checkIfKeysExist($arrayHasStoryMessageAsKey, $keys)) { // return false
    echo "arrayHasStoryMessageAsKey has all the keys<br />";
} else {
    echo "arrayHasStoryMessageAsKey does NOT have all the keys<br />";
}

if (checkIfKeysExist($arrayHasNone, $keys)) { // return false
    echo "arrayHasNone has all the keys<br />";
} else {
    echo "arrayHasNone does NOT have all the keys<br />";
}

Я предполагаю, что вам нужно проверить наличие нескольких ключей, ВСЕ СУЩЕСТВУЮЩИХ в массиве. Если вы ищете совпадение хотя бы одного ключа, дайте мне знать, чтобы я мог предоставить другую функцию.

Кодовая панель здесь http://codepad.viper-7.com/AKVPCH

Ким Стэкс
источник
1
Решение прекрасное, но там похоронен красивый однострочный драгоценный камень:if (0 === count(array_diff(['key1','key2','key3'], array_keys($lookIn)))) { // all keys exist } else { // nope }
Марк Фокс
То, что вы пишете, правда. Я считаю свою функцию более читаемой, хотя и многословной. Конечно, я мог ошибаться. Спасибо за комментарий к моему ответу. Узнаю что-то новое.
Kim Stacks
1

Надеюсь это поможет:

function array_keys_exist($searchForKeys = array(), $inArray = array()) {
    $inArrayKeys = array_keys($inArray);
    return count(array_intersect($searchForKeys, $inArrayKeys)) == count($searchForKeys); 
}
К-Алекс
источник
1

Это старый и, вероятно, будет похоронен, но это моя попытка.

У меня была проблема, похожая на @Ryan. В некоторых случаях мне нужно было только проверить, есть ли хотя бы 1 ключ в массиве, а в некоторых случаях все необходимое присутствовать.

Итак, я написал эту функцию:

/**
 * A key check of an array of keys
 * @param array $keys_to_check An array of keys to check
 * @param array $array_to_check The array to check against
 * @param bool $strict Checks that all $keys_to_check are in $array_to_check | Default: false
 * @return bool
 */
function array_keys_exist(array $keys_to_check, array $array_to_check, $strict = false) {
    // Results to pass back //
    $results = false;

    // If all keys are expected //
    if ($strict) {
        // Strict check //

        // Keys to check count //
        $ktc = count($keys_to_check);
        // Array to check count //
        $atc = count(array_intersect($keys_to_check, array_keys($array_to_check)));

        // Compare all //
        if ($ktc === $atc) {
            $results = true;
        }
    } else {
        // Loose check - to see if some keys exist //

        // Loop through all keys to check //
        foreach ($keys_to_check as $ktc) {
            // Check if key exists in array to check //
            if (array_key_exists($ktc, $array_to_check)) {
                $results = true;
                // We found at least one, break loop //
                break;
            }
        }
    }

    return $results;
}

Это было намного проще , чем кратное записи ||и &&блоки.

Каспер Уилкс
источник
0

Это не работает?

array_key_exists('story', $myarray) && array_key_exists('message', $myarray)
киви
источник
2
Константы не могут быть массивами ... :)
Sven
Я всегда забываю $, когда не пишу в моем суперкоде, проверяющем автозаполнение IDE. =)
Kiwi
0
<?php

function check_keys_exists($keys_str = "", $arr = array()){
    $return = false;
    if($keys_str != "" and !empty($arr)){
        $keys = explode(',', $keys_str);
        if(!empty($keys)){
            foreach($keys as $key){
                $return = array_key_exists($key, $arr);
                if($return == false){
                    break;
                }
            }
        }
    }
    return $return;
}

// запускаем демонстрацию

$key = 'a,b,c';
$array = array('a'=>'aaaa','b'=>'ccc','c'=>'eeeee');

var_dump( check_keys_exists($key, $array));
Вуонг
источник
0

Я не уверен, что это плохая идея, но я использую очень простой цикл foreach для проверки нескольких ключей массива.

// get post attachment source url
$image     = wp_get_attachment_image_src(get_post_thumbnail_id($post_id), 'single-post-thumbnail');
// read exif data
$tech_info = exif_read_data($image[0]);

// set require keys
$keys = array('Make', 'Model');

// run loop to add post metas foreach key
foreach ($keys as $key => $value)
{
    if (array_key_exists($value, $tech_info))
    {
        // add/update post meta
        update_post_meta($post_id, MPC_PREFIX . $value, $tech_info[$value]);
    }
} 
Любитель кода
источник
0
// sample data
$requiredKeys = ['key1', 'key2', 'key3'];
$arrayToValidate = ['key1' => 1, 'key2' => 2, 'key3' => 3];

function keysExist(array $requiredKeys, array $arrayToValidate) {
    if ($requiredKeys === array_keys($arrayToValidate)) {
        return true;
    }

    return false;
}
j4r3k
источник
0
$myArray = array('key1' => '', 'key2' => '');
$keys = array('key1', 'key2', 'key3');
$keyExists = count(array_intersect($keys, array_keys($myArray)));

Вернет true, потому что в $ myArray есть ключи из массива $ keys

Эндрю Лука
источник
0

Что-то как это можно использовать

//Say given this array
$array_in_use2 = ['hay' => 'come', 'message' => 'no', 'story' => 'yes'];
//This gives either true or false if story and message is there
count(array_intersect(['story', 'message'], array_keys($array_in_use2))) === 2;

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

Это решение может быть неэффективным, но оно работает!

Обновления

В одной жировой функции:

 /**
 * Like php array_key_exists, this instead search if (one or more) keys exists in the array
 * @param array $needles - keys to look for in the array
 * @param array $haystack - the <b>Associative</b> array to search
 * @param bool $all - [Optional] if false then checks if some keys are found
 * @return bool true if the needles are found else false. <br>
 * Note: if hastack is multidimentional only the first layer is checked<br>,
 * the needles should <b>not be<b> an associative array else it returns false<br>
 * The array to search must be associative array too else false may be returned
 */
function array_keys_exists($needles, $haystack, $all = true)
{
    $size = count($needles);
    if($all) return count(array_intersect($needles, array_keys($haystack))) === $size;
    return !empty(array_intersect($needles, array_keys($haystack)));

}

Так, например, вот так:

$array_in_use2 = ['hay' => 'come', 'message' => 'no', 'story' => 'yes'];
//One of them exists --> true
$one_or_more_exists = array_keys_exists(['story', 'message'], $array_in_use2, false);
//all of them exists --> true
$all_exists = array_keys_exists(['story', 'message'], $array_in_use2);

Надеюсь это поможет :)

Олуватоби Сэмюэл Омисакин
источник
0

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

для вызова моей функции я буду использовать 2 массива, подобные этому

validatePost(['username', 'password', 'any other field'], $_POST))

тогда моя функция будет выглядеть так

 function validatePost($requiredFields, $post)
    {
        $validation = [];

        foreach($requiredFields as $required => $key)
        {
            if(!array_key_exists($key, $post))
            {
                $validation['required'][] = $key;
            }
        }

        return $validation;
    }

это выведет это

"обязательно": ["имя пользователя", "пароль", "любое другое поле"]

поэтому эта функция проверяет и возвращает все недостающие поля почтового запроса.

Jerryurenaa
источник