Перевірка, чи є елементи одного масиву в іншому масиві в 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
)

Як я можу перевірити, чи хтось із елементів People знаходиться у масиві розшукуваних злочинців ?

У цьому прикладі він повинен повернутися trueтому, що 20знаходиться в розшуку злочинців .

Відповіді:


204

Можна використовувати array_intersect().

$result = !empty(array_intersect($people, $criminals));

8
Неможливо використовувати порожній () з будь-яким іншим, крім змінної.
grantwparks

@grantwparks, то чому в PHP-документах про цю функцію вони говорять "Повертається ЛАЖНО, якщо var існує і має порожнє, ненульове значення. В іншому випадку повертає TRUE. Наступні речі вважаються порожніми: array () (порожній масив ) "? Джерело: php.net/manual/en/function.empty.php
Пере

5
На сторінці, на яку ви пов’язані: "До PHP 5.5, порожній () підтримує лише змінні; будь-що інше призведе до помилки розбору. Іншими словами, наступне не буде працювати: порожній (обрізка ($ ім'я)). Натомість, використовувати обрізку ($ name) == false. "
grantwparks

9
Як згадувалося в коментарях, я виявив, що !empty це працює не так, як очікувалося . Натомість я використав count():!count(array_intersect($people, $criminals));
Mattios550

3
Чому це позначається як відповідь на 65 голосів, коли вона призводить до фатальної помилки: Неможливо використовувати функцію повернення значення в контексті запису?
Дейв Хек

31

Мало поганого використання array_intersect () та count () (замість порожнього).

Наприклад:

$bFound = (count(array_intersect($criminals, $people))) ? true : false;

2
У цьому немає нічого поганого, але count()він не вважається виконавцем (якщо ви піклуєтесь про мікрооптимізацію, тобто)
Джейк А. Сміт

23

якщо "порожній" - не найкращий вибір, що робити з цим:

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

або

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

22

Цей код недійсний, оскільки ви можете передавати змінні лише в мовні конструкції. empty()- це мовна конструкція.

Це потрібно зробити у двох рядках:

$result = array_intersect($people, $criminals);
$result = !empty($result);

Проблема не в тому, що це мовна конструкція. Проблема полягає в тому, що вона очікує посилання і Грег передає значення.
Артефакто

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

issetще швидше. І ви можете використовувати bool val, щоб увімкнути або вимкнути. Також пошукові значення як ключові переконайтеся, що немає дублікатів. 'Середній_пересіч середня: 0,52077736854553; in_array сер .: 0,015597295761108; середня сума: 0,0077081203460693´
коттон

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, хоча і незначно.


Результати обманюють. Запустити його лише один раз занадто швидко, щоб виміряти будь-яку різницю. Якщо у вас є сотні чи тисячі запитів в секунду, ці частки секунди швидко складаються, тому, якщо ви вважаєте, що ваша програма потребує масштабування, я б дотримувався 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);

Прошу вибачення за специфічний для PHP-код Laravel код, але я покину його, тому що, думаю, ви можете прочитати його як псевдо-код. Важлива частина - це два 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.

Удачі у вашій подорожі.


Я поняття не маю, що це стосується питання, але я придивився і: я дуже сподіваюся, що НІКОЛИ не використовуватиме динамічні дані $SEARCHABLE_TABLE_COLUMNS! Це кричить про ін'єкцію - незалежно від того, чи існує "надбуд захищений конструктор рамкових запитів" між цим, який намагається замаскувати і фільтрувати таблиці та рядки стовпців! У кінці таблиці та рядки стовпців не можна додати через заповнювач (підготовлені оператори), а їх потрібно вставити безпосередньо як SELECT ... FROM {$table} WHERE {$column} = :placeholder ..... Ofc залежить від адаптерів (mysql, mongo, ...), але це не аргумент для збереження! Pls статичний або немає списку =)
коттон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.