ตรวจสอบว่าองค์ประกอบของอาร์เรย์หนึ่งอยู่ในอาร์เรย์อื่นใน 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
ไม่สามารถใช้ empty () กับสิ่งอื่นนอกจากตัวแปร
Grantwparks

@grantwparks แล้วทำไมใน PHP docs เกี่ยวกับฟังก์ชันนี้ถึงบอกว่า "Returns FALSE ถ้า var มีอยู่และมีค่า non-empty ไม่ใช่ศูนย์มิฉะนั้นจะคืนค่า TRUE สิ่งต่อไปนี้จะถือว่าว่างเปล่า: array () (อาร์เรย์ว่าง ) "? ที่มา: php.net/manual/en/function.empty.php
Pere

5
จากหน้าที่คุณเชื่อมโยงไป: "ก่อนหน้า PHP 5.5, empty () รองรับตัวแปรเท่านั้นสิ่งอื่นใดจะทำให้เกิดข้อผิดพลาดในการแยกวิเคราะห์กล่าวอีกนัยหนึ่งสิ่งต่อไปนี้จะใช้ไม่ได้: ว่างเปล่า (trim ($ name)) แทน ใช้ trim ($ name) == false "
Grantwparks

9
ตามที่กล่าวไว้ในความคิดเห็นฉันพบ!empty ว่าไม่ได้ผลตามที่คาดไว้ แต่ฉันใช้count():!count(array_intersect($people, $criminals));
Mattios550

3
เหตุใดสิ่งนี้จึงถูกทำเครื่องหมายเป็นคำตอบด้วยคะแนนโหวต 65 คะแนนเมื่อมีข้อผิดพลาดร้ายแรง: ไม่สามารถใช้ค่าส่งคืนฟังก์ชันในบริบทการเขียนได้?
Dave Heq

31

มีข้อผิดพลาดเล็กน้อยในการใช้ array_intersect () และ count () (แทนที่จะว่างเปล่า)

ตัวอย่างเช่น:

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

2
ไม่มีอะไรผิดปกติ แต่count()ไม่ถือว่าเป็นนักแสดง (ถ้าคุณสนใจเกี่ยวกับการเพิ่มประสิทธิภาพไมโครนั่นคือ)
Jake

ตัวดำเนินการ ternary ซ้ำซ้อนในรหัสนี้คุณสามารถแยกวิเคราะห์เป็นบูลได้อย่างตรงไปตรงมาโดยเรียก (บูล) หน้า count
Ben Gooding

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

ปัญหาไม่ได้อยู่ที่โครงสร้างภาษา ปัญหาคือคาดว่าจะมีการอ้างอิงและ Greg's ส่งผ่านค่า
Artefacto

1
@Artefacto จาก php.net "หมายเหตุ: เนื่องจากนี่เป็นการสร้างภาษาไม่ใช่ฟังก์ชันจึงไม่สามารถเรียกใช้ฟังก์ชันตัวแปรได้" มันเหมือนกับที่พอลพูด
Grantwparks

17

การทดสอบประสิทธิภาพสำหรับ in_array เทียบกับ 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 เพื่อเปิดหรือปิด นอกจากนี้ค่าการค้นหาเป็นคีย์ตรวจสอบให้แน่ใจว่าไม่มีรายการที่ซ้ำกัน ´array_intersect เฉลี่ย: 0.52077736854553; in_array เฉลี่ย: 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และตรวจสอบประสิทธิภาพของแต่ละส่วน สิ่งที่น่าสนใจคือเวลา CPU ทั้งหมดเช่นเวลาของผู้ใช้ + เวลาของระบบจะเท่ากันสำหรับ PHP5.6 และหน่วยความจำก็เหมือนกัน เวลา CPU ทั้งหมดภายใต้ PHP5.4 นั้นน้อยกว่าสำหรับ in_array กว่า array_intersect แม้ว่าจะเล็กน้อยก็ตาม


ผลลัพธ์ที่ได้คือการหลอกลวง การเรียกใช้เพียงครั้งเดียวเร็วเกินไปที่จะวัดความแตกต่างใด ๆ หากคุณมีคำขอหลายร้อยหรือหลายพันรายการต่อวินาทีเศษส่วนของวินาทีเหล่านั้นจะเพิ่มขึ้นอย่างรวดเร็วดังนั้นหากคุณคิดว่าแอปพลิเคชันของคุณต้องปรับขนาดฉันจะยึดติดกับการin_arrayใช้
Frank Forte

1

นี่คือวิธีที่ฉันทำหลังจากค้นคว้ามาระยะหนึ่ง ฉันต้องการสร้างจุดสิ้นสุด Laravel API ที่ตรวจสอบว่าฟิลด์ "ใช้งานอยู่" หรือไม่ดังนั้นข้อมูลที่สำคัญคือ 1) ตาราง DB ใด 2) คอลัมน์ DB อะไร และ 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เพราะไม่ได้อยู่ในรายชื่อของคอลัมน์ได้รับอนุญาตให้passworduser

ขอให้โชคดีในการเดินทาง


ฉันไม่รู้ว่ามันเกี่ยวข้องอะไรกับคำถามนี้ แต่ฉันลองดูแล้ว: ฉันหวังว่าคุณจะไม่ใช้ข้อมูลแบบไดนามิก$SEARCHABLE_TABLE_COLUMNS! เสียงกรีดร้องสำหรับการฉีด - ไม่ว่าจะมี "ตัวสร้างการสืบค้นเฟรมเวิร์กที่ปลอดภัยเป็นพิเศษ" ระหว่างที่พยายามมาสก์และกรองสตริงตารางและคอลัมน์ก็ตาม! ในตอนท้ายตารางและคอลัมน์สตริงไม่สามารถเพิ่มผ่านทางตัวยึด (จัดทำงบ) SELECT ... FROM {$table} WHERE {$column} = :placeholder ....และจะต้องแทรกโดยตรงเช่น Ofc ขึ้นอยู่กับอะแดปเตอร์ (mysql, mongo, ... ) แต่นั่นไม่ใช่ข้อโต้แย้งที่จะบันทึก! กรุณาคงที่หรือไม่มีรายการ =)
cottton
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.