Como verificar se existem várias chaves de array

87

Eu tenho uma variedade de matrizes que irão conter

story & message

ou apenas

story

Como eu verificaria se um array contém a história e a mensagem? array_key_exists()procura apenas por aquela única chave na matriz.

Existe uma maneira de fazer isso?

Ryan
fonte
2
Se "história" estiver lá em qualquer um dos casos, parece que você realmente só precisa verificar se há "mensagem".
Wyzard
5
Usando array_intersect_key()compare um array das chaves que você deseja verificar com o array que você está verificando. Se o comprimento da saída for igual ao conjunto de chaves a serem verificadas, elas estão todas presentes.
Michael Berkowski
Wyzard, tenho outros arrays que contêm mensagem, mas não história, mas esses têm outras chaves que um array com história ou história e mensagem conteriam apenas. Obrigado
Ryan
Você está confundindo chaves e valores aqui? A matriz está formatada como ["story & message" => "value"]ou mais parecida com["story & message"]
GordonM

Respostas:

69

Se você tiver apenas 2 chaves para verificar (como na pergunta original), provavelmente é fácil chamar array_key_exists()duas vezes para verificar se as chaves existem.

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

No entanto, isso obviamente não se ajusta bem a muitas tonalidades. Nessa situação, uma função personalizada ajudaria.

function array_keys_exists(array $keys, array $arr) {
   return !array_diff_key(array_flip($keys), $arr);
}
alex
fonte
3
Se as pessoas acharem que as outras soluções são melhores para verificar se um array tem dois membros presentes, elas não devem gostar de código legível ou desempenho claro :)
alex
Esta é provavelmente a solução mais simples se as chaves necessárias forem relativamente poucas. Ele se tornará ilegível se eles forem algo como 20 ou 30.
apokryfos
1
@apokryfos Concordo, mas responde à pergunta do OP.
alex
2
@alex o único problema é que se $keyscontém um elemento que não está em$arr e outro que está nele, !array_diff_keyretorna vazio => false( exemplo 3v4l ) ...
CPHPython
3
Acho que isso pode ser mais legível com o uso, !array_diff($keys, array_keys($array));porque há um pouco menos de carga cognitiva envolvida no trabalho com esses array_flips.
moopet
193

Aqui está uma solução que é escalonável, mesmo se você quiser verificar um grande número de chaves:

<?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!
}
Erfan
fonte
Eu gostaria de saber a razão pela qual isso foi rejeitado .. afaik isso é mais rápido porque array_intersect_key é implementado em C e você não precisa de um loop
Erfan
Muito inteligente, na verdade, muito bem - embora um pouco difícil de ler.
Jon z
Obrigado :) É estranho que o PHP não tenha uma função embutida para fazer isso - é bastante comum. Existem toneladas de classes de validação de entrada do usuário que fazem isso, mas para a maioria dos casos de uso é um exagero
Erfan,
12
Solução inteligente, de fato, mas é realmente mais lenta (cerca de 50% mais lenta na minha caixa) do que um simples: `` `$ ok = true; foreach ($ obrigatório como $ campo) {if (! array_key_exists ($ field, $ data)) $ ok = false; }
Ozh
@Ozh além de array_key_exists é mais lento que isset
iautomation
34

Surpreendentemente array_keys_existnão existe ?! Nesse ínterim, isso deixa algum espaço para descobrir uma expressão de linha única para essa tarefa comum. Estou pensando em um script de shell ou outro pequeno programa.

Nota: cada uma das seguintes soluções usa […]sintaxe concisa de declaração de array disponível no php 5.4+

array_diff + array_keys

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

(gorjeta para Kim Stacks )

Essa abordagem é a mais breve que encontrei. array_diff()retorna uma matriz de itens presentes no argumento 1 não presentes no argumento2. Portanto, uma matriz vazia indica que todas as chaves foram encontradas. No php 5.5 você pode simplificar 0 === count(…)para ser simples 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
}

Mais difícil de ler, fácil de mudar. array_reduce()usa um retorno de chamada para iterar em uma matriz para chegar a um valor. Ao alimentar as chaves, estamos interessados ​​no $initialvalor de $ine, em seguida, remover as chaves encontradas na origem, podemos esperar terminar com 0 elementos se todas as chaves forem encontradas.

A construção é fácil de modificar, pois as teclas em que estamos interessados ​​se encaixam perfeitamente no resultado final.

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
}

Mais simples de escrever do que a array_reducesolução, mas um pouco mais complicada de editar. array_filtertambém é um retorno de chamada iterativo que permite criar uma matriz filtrada retornando verdadeiro (copiar o item para a nova matriz) ou falso (não copiar) no retorno de chamada. O ponto principal é que você deve alterar 2o número de itens que espera.

Isso pode ser mais durável, mas chega a uma legibilidade absurda:

$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
}
Mark Fox
fonte
3
a diferença será insignificante para pequenos conjuntos. se você está escrevendo uma biblioteca / estrutura que lida com grandes conjuntos de dados, provavelmente deve testar o desempenho de cada unidade para encontrar gargalos em vez de otimizar prematuramente.
Mark Fox
16

Parece-me que o método mais fácil de longe seria este:

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

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

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

Impressões:

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

Isso também permite verificar quais chaves estão faltando exatamente. Isso pode ser útil para tratamento de erros.

Petr Cibulka
fonte
É para isso que vim aqui!
eNeMetcH
8

Mais uma solução possível:

if (!array_diff(['story', 'message'], array_keys($array))) {
    // OK: all the keys are in $array
} else {
   // FAIL: some keys are not
}
Vasily
fonte
7

As soluções acima são inteligentes, mas muito lentas. Um loop foreach simples com isset é duas vezes mais rápido que a array_intersect_keysolução.

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

(344ms vs 768ms para 1.000.000 de iterações)

iautomação
fonte
isset retornará falso se ['chave' => nulo] e às vezes você tem arrays com valores nulos. Você deve usar array_key_exists em vez de isset
j4r3k
Tive que usar o oposto aqui por causa do retorno prematuro com false( falsesubstituições trueneste caso). Então, o que funciona para minhas necessidades são foreach ($keys as $key) { if (array_key_exists($key, $array)) { return true; }} return false;Minhas necessidades se a anychave em um array existe em outro array ...
Geoff
1
Eu não chamaria +/- 400ms acima de um milhão de teclas de "muito lento", mas sou apenas humano!
coronelclick
3

Se você tem algo assim:

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

Você poderia simplesmente count():

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

Isso só funciona se você tiver certeza de que possui SOMENTE essas chaves de array e nada mais.

O uso de array_key_exists () suporta apenas a verificação de uma chave por vez, portanto, você precisará verificar as duas separadamente:

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()retorna true se a chave estiver presente na matriz, mas é uma função real e muito para digitar. A construção da linguagem isset()fará quase o mesmo, exceto se o valor testado for NULL:

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

Além disso, o isset permite verificar várias variáveis ​​ao mesmo tempo:

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

Agora, para otimizar o teste para o que está definido, é melhor usar este "se":

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
  }
}
Sven
fonte
3

Que tal isso:

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

só retorna verdadeiro se ambos não forem nulos

se for nulo, a chave não está na matriz

David Dutkovsky
fonte
1
se o valor de $arr['key1']ou $arr['key2']for null, o código existirá, a chave ainda existe.
Xorifelse
Eu escrevi um teste, por favor, dê uma olhada nele teste @Xorifelse e me corrija se eu estiver errado. Para sua informação: naquela época eu conhecia apenas a versão 5.6. * Do PHP, então conhecia apenas a versão.
David Dutkovsky
O que esse código está tentando realizar? Por que você não está apenas usando um foreachloop?
Xorifelse
Eu queria adicionar uma prova de que a issetfunção funciona como eu pretendia, mas agora percebo que você estava certo, as chaves ainda permanecem em uma matriz e, portanto, minha resposta não está correta, obrigado pelo feedback. Sim, eu poderia usar isso foreach.
David Dutkovsky
3

Eu uso algo assim com bastante frequência

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

ou para encontrar os valores para as chaves desejadas

$wantedValues = array_intersect_key($source, array_fill_keys($wantedKeys, 1))
Steve
fonte
2

tente isso

$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
  }
Valmayaki
fonte
1

Esta é a função que escrevi para mim mesmo para usar em uma classe.

<?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 />";
}

Estou assumindo que você precisa verificar se há várias chaves TODAS EXISTENTES em uma matriz. Se você estiver procurando uma correspondência de pelo menos uma chave, me avise para que eu possa fornecer outra função.

Codepad aqui http://codepad.viper-7.com/AKVPCH

Kim Stacks
fonte
1
A solução é boa, mas há uma bela joia de uma linha enterrada:if (0 === count(array_diff(['key1','key2','key3'], array_keys($lookIn)))) { // all keys exist } else { // nope }
Mark Fox,
O que você escreve é ​​verdade. Eu acho minha função mais legível, embora detalhada. Claro, posso estar enganado. Obrigado por comentar minha resposta. Eu aprendo algo novo.
Kim Stacks de
1

Espero que isto ajude:

function array_keys_exist($searchForKeys = array(), $inArray = array()) {
    $inArrayKeys = array_keys($inArray);
    return count(array_intersect($searchForKeys, $inArrayKeys)) == count($searchForKeys); 
}
K-Alex
fonte
1

Isso é antigo e provavelmente será enterrado, mas esta é minha tentativa.

Tive um problema semelhante ao do @Ryan. Em alguns casos, só precisei verificar se pelo menos 1 chave estava em uma matriz e, em alguns casos, todas precisavam estar presentes.

Então, escrevi esta função:

/**
 * 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;
}

Isso foi muito mais fácil do que escrever vários blocos ||e &&.

Casper Wilkes
fonte
0

Isso não funciona?

array_key_exists('story', $myarray) && array_key_exists('message', $myarray)
kiwi
fonte
2
Constantes não podem ser matrizes ... :)
Sven
Eu sempre esqueço o $ quando não estou escrevendo em meu super código, verificando o autocomplete 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;
}

// executar demonstração

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

var_dump( check_keys_exists($key, $array));
Vuong
fonte
0

Não tenho certeza se é uma má ideia, mas uso um loop foreach muito simples para verificar várias chaves de array.

// 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]);
    }
} 
Amante do código
fonte
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
fonte
0
$myArray = array('key1' => '', 'key2' => '');
$keys = array('key1', 'key2', 'key3');
$keyExists = count(array_intersect($keys, array_keys($myArray)));

Retornará verdadeiro, porque há chaves do array $ keys em $ myArray

Andrew Luca
fonte
0

Algo como isso poderia ser usado

//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;

Observe a comparação com 2, se os valores que deseja pesquisar forem diferentes, você pode alterar.

Esta solução pode não ser eficiente, mas funciona!

Atualizações

Em uma função de gordura :

 /**
 * 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)));

}

Então, por exemplo com isto:

$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);

Espero que isto ajude :)

Oluwatobi Samuel Omisakin
fonte
0

Eu costumo usar uma função para validar minha postagem e é uma resposta para essa pergunta também, então deixe-me postar.

para chamar minha função vou usar o array 2 assim

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

então minha função ficará assim

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

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

        return $validation;
    }

isso vai gerar isso

"obrigatório": ["nome de usuário", "senha", "qualquer outro campo"]

então o que essa função faz é validar e retornar todos os campos ausentes da solicitação de postagem.

Jerryurenaa
fonte