Memeriksa untuk melihat apakah satu elemen array ada di array lain di PHP

130

Saya memiliki dua array dalam PHP sebagai berikut:

Orang-orang:

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

Penjahat yang Dicari:

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
)

Bagaimana cara memeriksa apakah salah satu Orang elemen dalam Penjahat Dicari array yang?

Dalam contoh ini, itu harus kembali truekarena 20ada di Wanted Criminals .

Philip Morton
sumber

Jawaban:

204

Anda bisa menggunakan array_intersect().

$result = !empty(array_intersect($people, $criminals));
Greg
sumber
8
Tidak dapat menggunakan empty () dengan apa pun selain variabel.
grantwparks
@grantwparks lalu mengapa dalam dokumen PHP tentang fungsi ini mereka mengatakan "Mengembalikan FALSE jika var ada dan memiliki nilai non-kosong, bukan-nol. Jika tidak mengembalikan TRUE. Hal-hal berikut dianggap kosong: array () (array kosong ) "? Sumber: php.net/manual/en/function.empty.php
Pere
5
Dari halaman yang Anda tautkan: "Sebelum PHP 5.5, empty () hanya mendukung variabel; hal lain akan menghasilkan kesalahan parse. Dengan kata lain, berikut ini tidak akan berfungsi: kosong (trim ($ name)). Sebagai gantinya, gunakan trim ($ name) == false. "
Grantwparks
9
Seperti disebutkan dalam komentar saya menemukan itu !empty tidak berfungsi seperti yang diharapkan . Sebaliknya, saya menggunakan count():!count(array_intersect($people, $criminals));
Mattios550
3
Mengapa ini ditandai sebagai jawaban dengan 65 suara naik ketika muncul kesalahan Fatal: Tidak dapat menggunakan nilai pengembalian fungsi dalam konteks tulis?
Dave Heq
31

Ada sedikit yang salah dengan menggunakan array_intersect () dan count () (bukan kosong).

Sebagai contoh:

$bFound = (count(array_intersect($criminals, $people))) ? true : false;
papsy
sumber
2
Tidak ada yang salah dengan itu tetapi count()tidak dianggap berkinerja (jika Anda peduli dengan pengoptimalan mikro, itulah)
Jake A. Smith
Operator terner berlebihan dalam kode ini, Anda dapat langsung menguraikannya sebagai bool, dengan memanggil (bool) di depan hitungan
Ben Gooding
23

jika 'kosong' bukan pilihan terbaik, bagaimana dengan ini:

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

atau

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

Kode itu tidak valid karena Anda hanya dapat mengirimkan variabel ke dalam konstruksi bahasa. empty()adalah konstruksi bahasa.

Anda harus melakukan ini dalam dua baris:

$result = array_intersect($people, $criminals);
$result = !empty($result);
Paul Dragoonis
sumber
Masalahnya bukan itu adalah konstruksi bahasa. Masalahnya adalah ia mengharapkan referensi dan Greg memberikan nilai.
Artefacto
1
@Artefacto, dari php.net "Catatan: Karena ini adalah konstruksi bahasa dan bukan fungsi, ini tidak dapat dipanggil menggunakan fungsi variabel." Persis seperti yang dikatakan Paul.
Grantwparks
17

Uji kinerja untuk 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;

Berikut hasilnya:

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 setidaknya 5 kali lebih cepat. Perhatikan bahwa kami "istirahat" segera setelah hasilnya ditemukan.

Frank Forte
sumber
Terima kasih untuk patokannya. Jadi, jika Anda tahu Anda menangani array kecil, lebih baik tetap menggunakannya array_intersect().
Tokeeen.com
issetbahkan lebih cepat. Dan Anda dapat menggunakan bool val untuk mengaktifkan atau menonaktifkan. Juga nilai pencarian sebagai kunci pastikan tidak ada duplikat. ´array_intersect rata-rata: 0,52077736854553; in_array rata-rata: 0,015597295761108; rata-rata isset: 0,0077081203460693´
cottton
1

Anda juga bisa menggunakan in_array sebagai berikut:

<?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) }

Meskipun array_intersect tentu lebih nyaman digunakan, ternyata performanya tidak terlalu unggul. Saya membuat skrip ini juga:

<?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) }

Kemudian, saya menjalankan kedua cuplikan di: http://3v4l.org/WGhO7/perf#tabs dan http://3v4l.org/g1Hnu/perf#tabs dan memeriksa kinerja masing-masing. Hal yang menarik adalah total waktu CPU, yaitu waktu pengguna + waktu sistem sama untuk PHP5.6 dan memori juga sama. Total waktu CPU di bawah PHP5.4 lebih sedikit untuk in_array daripada array_intersect, meskipun sedikit.

slevy1
sumber
Hasilnya menipu. Menjalankannya hanya satu kali terlalu cepat untuk mengukur perbedaan apa pun. Jika Anda memiliki ratusan atau ribuan permintaan per detik, sepersekian detik tersebut bertambah dengan cepat, jadi jika menurut Anda aplikasi Anda perlu diskalakan, saya akan tetap in_arraymenerapkannya.
Frank Forte
1

Inilah cara saya melakukannya setelah menelitinya untuk sementara waktu. Saya ingin membuat titik akhir API Laravel yang memeriksa apakah suatu bidang "sedang digunakan", jadi informasi pentingnya adalah: 1) tabel DB yang mana? 2) apa kolom DB? dan 3) apakah ada nilai di kolom itu yang cocok dengan istilah pencarian?

Mengetahui hal ini, kita dapat membuat array asosiatif kita:

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

Kemudian, kita dapat menetapkan nilai yang akan kita periksa:

$table = 'users';
$column = 'email';
$value = '[email protected]';

Kemudian, kita dapat menggunakan array_key_exists()dan in_array()dengan satu sama lain untuk mengeksekusi kombo satu, dua langkah dan kemudian bertindak berdasarkan truthykondisi:

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

Saya minta maaf untuk kode PHP khusus Laravel, tetapi saya akan membiarkannya karena saya rasa Anda dapat membacanya sebagai kode semu. Bagian penting adalah dua ifpernyataan yang dieksekusi secara sinkron.

array_key_exists()dan in_array()merupakan fungsi PHP.

sumber:

Yang menyenangkan tentang algoritma yang saya menunjukkan di atas adalah bahwa Anda dapat membuat endpoint SISA seperti GET /in-use/{table}/{column}/{value}(di mana table, columndanvalue adalah variabel).

Kamu bisa saja:

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

dan kemudian Anda dapat membuat permintaan GET seperti:

GET /in-use/accounts/account_name/Bob's Drywall (Anda mungkin perlu menyandikan uri bagian terakhir, tetapi biasanya tidak)

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

GET /in-use/users/email/[email protected]

Perhatikan juga bahwa tidak ada yang bisa melakukan:

GET /in-use/users/password/dogmeat1337karena passwordtidak tercantum dalam daftar kolom yang diizinkan untuk user.

Semoga berhasil dalam perjalanan Anda.

agm1984
sumber
Saya tidak tahu apa hubungannya ini dengan pertanyaan tetapi saya melihatnya dan: saya sangat berharap Anda TIDAK PERNAH menggunakan data dinamis $SEARCHABLE_TABLE_COLUMNS! Ini berteriak untuk injeksi - tidak peduli apakah ada "pembuat kueri kerangka kerja yang sangat aman" antara yang mencoba untuk menutupi dan memfilter tabel dan string kolom! Di ujung tabel dan kolom, string tidak dapat ditambahkan melalui placeholder (pernyataan yang disiapkan) dan harus dimasukkan secara langsung seperti SELECT ... FROM {$table} WHERE {$column} = :placeholder ..... Ofc tergantung pada adaptor (mysql, mongo, ...) TAPI tidak ada alasan untuk menyelamatkan! Pls statis atau tidak ada daftar =)
cottton