Bir dizinin elemanlarının PHP'de başka bir dizide olup olmadığının kontrol edilmesi


130

PHP'de aşağıdaki gibi iki dizim var:

İnsanlar:

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

Aranan Suçlular:

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
)

Kişiler öğelerinden herhangi birinin Aranan Suçlular dizisinde olup olmadığını nasıl kontrol ederim ?

Bu örnekte, Aranan Suçlular'datrue olduğu 20için geri dönmelidir .

Yanıtlar:


204

Kullanabilirsiniz array_intersect().

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

8
Bir değişkenden başka bir şeyle empty () kullanılamaz.
grantwparks

@grantwparks öyleyse neden PHP belgelerinde bu işlevle ilgili "var varsa ve boş olmayan, sıfır olmayan bir değere sahipse FALSE döndürür. Aksi takdirde TRUE döndürür. Aşağıdaki şeyler boş kabul edilir: array () (boş bir dizi ) "? Kaynak: php.net/manual/en/function.empty.php
Pere

5
Bağlandığınız sayfadan: "PHP 5.5'ten önce, empty () yalnızca değişkenleri destekler; başka herhangi bir şey bir ayrıştırma hatasıyla sonuçlanır. Başka bir deyişle, aşağıdakiler çalışmaz: empty (trim ($ name)). Bunun yerine, trim ($ isim) == false kullanın. "
grantwparks

9
Yorumlarda belirtildiği !empty gibi beklendiği gibi çalışmadığını buldum . Bunun yerine şunu kullandım count():!count(array_intersect($people, $criminals));
Mattios550

3
Fatal error: İşlev dönüş değeri yazma bağlamında kullanılamaz mı?
Dave Heq

31

Array_intersect () ve count () (boş yerine) kullanmakta çok az yanlış var.

Örneğin:

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

2
Orada onunla sorun yok ama count()(sen olduğunu, mikro optimizasyonu veriyorsan) performant kabul edilmez
Jake A. Smith

Üçlü operatör bu kodda fazlalıktır, sayımın önünde (bool) 'u çağırarak doğrudan bir bool olarak ayrıştırabilirsiniz
Ben Gooding

23

'boş' en iyi seçenek değilse, buna ne dersiniz?

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

veya

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

22

Değişkenleri yalnızca dil yapılarına aktarabildiğiniz için bu kod geçersizdir. empty()bir dil yapısıdır.

Bunu iki satırda yapmalısınız:

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

Sorun, bunun bir dil yapısı olması değil. Sorun, bir referans beklemesi ve Greg'in bir değer iletmesidir.
Artefacto

1
@Artefacto, php.net'ten "Not: Bu bir dil yapısı olduğundan ve bir işlev olmadığından, değişken işlevler kullanılarak çağrılamaz." Paul'ün dediği gibi.
grantwparks

17

İn_array ve array_intersect için performans testi:

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

Sonuçlar burada:

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 en az 5 kat daha hızlıdır. Bir sonuç bulunur bulunmaz "kırıldığımızı" unutmayın.


Kıyaslama için teşekkürler. Dolayısıyla, küçük dizilerle çalıştığınızı biliyorsanız, devam etmeniz daha iyidir array_intersect().
Tokeeen.com

issetdaha da hızlı. Ve bool val'i etkinleştirmek veya devre dışı bırakmak için kullanabilirsiniz. Ayrıca anahtar olarak arama değerleri, yinelemelerin olmamasını sağlar. ´array_intersect ort: 0.52077736854553; in_array ort .: 0,015597295761108; isset ort .: 0.0077081203460693´
cottton

1

İn_array'i aşağıdaki gibi de kullanabilirsiniz:

<?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'in kullanımı kesinlikle daha uygun olsa da, performans açısından gerçekten üstün olmadığı ortaya çıktı. Bu komut dosyasını da ben oluşturdum:

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

Ardından, sırasıyla http://3v4l.org/WGhO7/perf#tabs ve http://3v4l.org/g1Hnu/perf#tabs adreslerinde her iki parçacığı da çalıştırdım ve her birinin performansını kontrol ettim. İlginç olan, toplam CPU zamanı, yani kullanıcı zamanı + sistem zamanı PHP5.6 için aynı ve bellek de aynı. PHP5.4 altındaki toplam CPU süresi, marjinal de olsa, in_array için array_intersect'ten daha azdır.


Sonuçlar aldatıcıdır. Sadece bir kez çalıştırmak, herhangi bir farkı ölçmek için çok hızlıdır. Saniyede yüzlerce veya binlerce isteğiniz varsa, saniyenin bu kesirleri hızla toplanır, bu nedenle uygulamanızın ölçeklenmesi gerektiğini düşünüyorsanız, uygulamaya bağlı kalırım in_array.
Frank Forte

1

İşte bir süre araştırdıktan sonra yaptığım bir yol. Bir alanın "kullanımda" olup olmadığını kontrol eden bir Laravel API uç noktası yapmak istedim, bu nedenle önemli bilgi: 1) hangi DB tablosu? 2) hangi DB sütunu? ve 3) bu sütunda arama terimleriyle eşleşen bir değer var mı?

Bunu bilerek, ilişkisel dizimizi oluşturabiliriz:

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

Ardından kontrol edeceğimiz değerlerimizi belirleyebiliriz:

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

Sonra, kullanabilir array_key_exists()ve in_array()birbirinden bir tane, iki aşamalı açılan yürütmek ve sonra üzerine hareket truthykoşulu:

// 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'e özgü PHP kodu için özür dilerim, ancak bırakacağım çünkü onu sözde kod olarak okuyabileceğinizi düşünüyorum. Önemliif olan, eşzamanlı olarak yürütülen iki deyimdir.

array_key_exists()ve in_array()PHP işlevleridir.

kaynak:

Yukarıda gösterdi algoritması hakkında güzel şey gibi REST son nokta yapmak olabilir GET /in-use/{table}/{column}/{value}(burada table, columnve valuedeğişkenler).

Şunlara sahip olabilirsiniz:

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

ve daha sonra aşağıdaki gibi GET istekleri yapabilirsiniz:

GET /in-use/accounts/account_name/Bob's Drywall (son bölümü kodlamanız gerekebilir, ancak genellikle değil)

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

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

Ayrıca kimsenin yapamayacağına dikkat edin:

GET /in-use/users/password/dogmeat1337Çünkü passwordizin sütunların listenizde yer almıyorsa user.

Yolculuğunuzda iyi şanslar.


Bunun soruyla ne ilgisi olduğu hakkında hiçbir fikrim yok ama bir göz attım ve: Umarım içinde ASLA dinamik verileri kullanmazsınız $SEARCHABLE_TABLE_COLUMNS! Bu, bir enjeksiyon için çığlık atıyor - tablo ve sütun dizelerini maskelemeye ve filtrelemeye çalışan "ultra güvenli çerçeve sorgu oluşturucu" olsa da! Sonunda tablo ve sütun dizeleri yer tutucu (hazırlanmış ifadeler) ile eklenemez ve doğrudan gibi eklenmelidir SELECT ... FROM {$table} WHERE {$column} = :placeholder ..... Ofc adaptörlere bağlıdır (mysql, mongo, ...) ANCAK bu kurtarılacak bir argüman değildir! Pls statik veya liste yok =)
cottton
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.