Проверка, находятся ли элементы одного массива в другом массиве в PHP

130

У меня есть два массива в PHP:

Люди:

Array
(
    [0] => 3
    [1] => 20
)

Разыскиваемые преступники:

Array
(
    [0] => 2
    [1] => 4
    [2] => 8
    [3] => 11
    [4] => 12
    [5] => 13
    [6] => 14
    [7] => 15
    [8] => 16
    [9] => 17
    [10] => 18
    [11] => 19
    [12] => 20
)

Как я могу проверить , если какой - либо из народных элементов в розыске массива?

В этом примере он должен вернуться, trueпотому что 20находится в розыске преступников .

Филип Мортон
источник

Ответы:

204

Вы можете использовать array_intersect().

$result = !empty(array_intersect($people, $criminals));
Greg
источник
8
Нельзя использовать empty () ни с чем, кроме переменной.
grantwparks
@grantwparks, тогда почему в документации PHP об этой функции говорится: «Возвращает FALSE, если var существует и имеет непустое ненулевое значение. В противном случае возвращает TRUE. Пустыми считаются следующие вещи: array () (пустой массив ) "? Источник: php.net/manual/en/function.empty.php
Пере
5
Со страницы, на которую вы ссылаетесь: «До PHP 5.5 empty () поддерживает только переменные; все остальное приведет к ошибке синтаксического анализа. Другими словами, следующее не будет работать: empty (trim ($ name)). Вместо этого, используйте trim ($ name) == false. "
grantwparks
9
Как упоминалось в комментариях, я обнаружил, что !empty это не работает должным образом . Вместо этого я использовал count():!count(array_intersect($people, $criminals));
Mattios550
3
Почему это помечается как ответ с 65 голосами, когда он вызывает фатальную ошибку: невозможно использовать возвращаемое значение функции в контексте записи?
Dave Heq
31

Нет ничего плохого в использовании array_intersect () и count () (вместо empty).

Например:

$bFound = (count(array_intersect($criminals, $people))) ? true : false;
papsy
источник
2
В этом нет ничего плохого, но count()он не считается производительным (если вы заботитесь о микрооптимизации)
Джейк А. Смит,
Тернарный оператор в этом коде избыточен, вы можете просто проанализировать его как логическое значение, вызвав (bool) перед count
Бен Гудинг
23

если «пустой» - не лучший выбор, как насчет этого:

if (array_intersect($people, $criminals)) {...} //when found

или

if (!array_intersect($people, $criminals)) {...} //when not found
Ihtus
источник
22

Этот код недействителен, поскольку вы можете передавать переменные только в языковые конструкции. empty()это языковая конструкция.

Это нужно сделать в две строчки:

$result = array_intersect($people, $criminals);
$result = !empty($result);
Пол Драгунис
источник
Проблема не в том, что это языковая конструкция. Проблема в том, что он ожидает ссылку и Грег передает значение.
Artefacto
1
@Artefacto, с php.net «Примечание: поскольку это языковая конструкция, а не функция, ее нельзя вызвать с помощью функций переменных». Это в точности так, как сказал Пол.
grantwparks
17

Тест производительности для in_array vs array_intersect:

$a1 = array(2,4,8,11,12,13,14,15,16,17,18,19,20);

$a2 = array(3,20);

$intersect_times = array();
$in_array_times = array();
for($j = 0; $j < 10; $j++)
{
    /***** TEST ONE array_intersect *******/
    $t = microtime(true);
    for($i = 0; $i < 100000; $i++)
    {
        $x = array_intersect($a1,$a2);
        $x = empty($x);
    }
    $intersect_times[] = microtime(true) - $t;


    /***** TEST TWO in_array *******/
    $t2 = microtime(true);
    for($i = 0; $i < 100000; $i++)
    {
        $x = false;
        foreach($a2 as $v){
            if(in_array($v,$a1))
            {
                $x = true;
                break;
            }
        }
    }
    $in_array_times[] = microtime(true) - $t2;
}

echo '<hr><br>'.implode('<br>',$intersect_times).'<br>array_intersect avg: '.(array_sum($intersect_times) / count($intersect_times));
echo '<hr><br>'.implode('<br>',$in_array_times).'<br>in_array avg: '.(array_sum($in_array_times) / count($in_array_times));
exit;

Вот результаты:

0.26520013809204
0.15600109100342
0.15599989891052
0.15599989891052
0.1560001373291
0.1560001373291
0.15599989891052
0.15599989891052
0.15599989891052
0.1560001373291
array_intersect avg: 0.16692011356354

0.015599966049194
0.031199932098389
0.031200170516968
0.031199932098389
0.031200885772705
0.031199932098389
0.031200170516968
0.031201124191284
0.031199932098389
0.031199932098389
in_array avg: 0.029640197753906

in_array как минимум в 5 раз быстрее. Обратите внимание, что мы «ломаемся», как только находим результат.

Фрэнк Форте
источник
Спасибо за тест. Так что, если вы знаете, что имеете дело с небольшими массивами, лучше остановиться array_intersect().
Tokeeen.com 02
issetеще быстрее. И вы можете использовать bool val для включения или отключения. Также убедитесь, что значения поиска в качестве ключа не имеют дубликатов. ´array_intersect avg: 0.52077736854553; in_array avg: 0,015597295761108; isset avg: 0.0077081203460693´
cottton
1

Вы также можете использовать in_array следующим образом:

<?php
$found = null;
$people = array(3,20,2);
$criminals = array( 2, 4, 8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
foreach($people as $num) {
    if (in_array($num,$criminals)) {
        $found[$num] = true;
    } 
}
var_dump($found);
// array(2) { [20]=> bool(true)   [2]=> bool(true) }

Хотя array_intersect, безусловно, более удобен в использовании, оказывается, что он не намного лучше с точки зрения производительности. Я тоже создал этот скрипт:

<?php
$found = null;
$people = array(3,20,2);
$criminals = array( 2, 4, 8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20);
$fastfind = array_intersect($people,$criminals);
var_dump($fastfind);
// array(2) { [1]=> int(20)   [2]=> int(2) }

Затем я запустил оба фрагмента соответственно по адресу: http://3v4l.org/WGhO7/perf#tabs и http://3v4l.org/g1Hnu/perf#tabs и проверил производительность каждого из них. Интересно то, что общее время процессора, т.е. время пользователя + системное время, одинаково для PHP5.6, и память также такая же. Общее время процессора в PHP5.4 меньше для in_array, чем array_intersect, хотя и незначительно.

slevy1
источник
Результаты обманчивы. Выполнить его только один раз слишком быстро, чтобы измерить разницу. Если у вас есть сотни или тысячи запросов в секунду, эти доли секунды складываются быстро, поэтому, если вы считаете, что ваше приложение нуждается в масштабировании, я бы остановился на in_arrayреализации.
Фрэнк Форте
1

Вот как я это делаю после некоторого исследования. Я хотел создать конечную точку API Laravel, которая проверяет, "используется ли" поле, поэтому важная информация: 1) какая таблица БД? 2) какой столбец БД? и 3) есть ли в этом столбце значение, соответствующее условиям поиска?

Зная это, мы можем построить наш ассоциативный массив:

$SEARCHABLE_TABLE_COLUMNS = [
    'users' => [ 'email' ],
];

Затем мы можем установить наши значения, которые мы будем проверять:

$table = 'users';
$column = 'email';
$value = 'alice@bob.com';

Затем мы можем использовать array_key_exists()и друг in_array()с другом для выполнения одно-, двухэтапного комбо, а затем действовать в соответствии с truthyусловием:

// step 1: check if 'users' exists as a key in `$SEARCHABLE_TABLE_COLUMNS`
if (array_key_exists($table, $SEARCHABLE_TABLE_COLUMNS)) {

    // step 2: check if 'email' is in the array: $SEARCHABLE_TABLE_COLUMNS[$table]
    if (in_array($column, $SEARCHABLE_TABLE_COLUMNS[$table])) {

        // if table and column are allowed, return Boolean if value already exists
        // this will either return the first matching record or null
        $exists = DB::table($table)->where($column, '=', $value)->first();

        if ($exists) return response()->json([ 'in_use' => true ], 200);
        return response()->json([ 'in_use' => false ], 200);
    }

    // if $column isn't in $SEARCHABLE_TABLE_COLUMNS[$table],
    // then we need to tell the user we can't proceed with their request
    return response()->json([ 'error' => 'Illegal column name: '.$column ], 400);
}

// if $table isn't a key in $SEARCHABLE_TABLE_COLUMNS,
// then we need to tell the user we can't proceed with their request
return response()->json([ 'error' => 'Illegal table name: '.$table ], 400);

Прошу прощения за специфичный для Laravel PHP-код, но я оставлю его, потому что я думаю, вы можете прочитать его как псевдокод. Важная часть - это два ifоператора, которые выполняются синхронно.

array_key_exists()и in_array()являются функциями PHP.

источник:

Хорошая вещь об алгоритме , который я показал выше, что вы можете сделать REST конечной точки , такие как GET /in-use/{table}/{column}/{value}(где table, columnи valueпеременные).

У вас могло быть:

$SEARCHABLE_TABLE_COLUMNS = [
    'accounts' => [ 'account_name', 'phone', 'business_email' ],
    'users' => [ 'email' ],
];

а затем вы можете делать запросы GET, такие как:

GET /in-use/accounts/account_name/Bob's Drywall (вам может потребоваться кодирование uri последней части, но обычно нет)

GET /in-use/accounts/phone/888-555-1337

GET /in-use/users/email/alice@bob.com

Заметьте также, что никто не может:

GET /in-use/users/password/dogmeat1337потому что passwordне указан в вашем списке разрешенных столбцов для user.

Удачи тебе в пути.

agm1984
источник
Я понятия не имею, какое отношение это имеет к вопросу, но я посмотрел и: я очень надеюсь, что вы НИКОГДА не используете динамические данные $SEARCHABLE_TABLE_COLUMNS! Это требует инъекции - независимо от того, есть ли между ними «сверхбезопасный конструктор запросов», который пытается замаскировать и отфильтровать строки таблиц и столбцов! В конце строки таблицы и столбца не могут быть добавлены через заполнитель (подготовленные операторы) и должны быть вставлены напрямую, как SELECT ... FROM {$table} WHERE {$column} = :placeholder ..... Ofc зависит от адаптеров (mysql, mongo, ...), НО это не аргумент для сохранения! Pls static or no list =)
cottton