วิธีค้นหาโดย key => ค่าในอาร์เรย์หลายมิติใน PHP


147

มีวิธีที่รวดเร็วในการรับ subarrays ทั้งหมดที่พบคู่ค่าคีย์ในอาร์เรย์หลายมิติหรือไม่? ฉันไม่สามารถบอกได้ว่าอาเรย์นั้นจะลึกแค่ไหน

อาร์เรย์ตัวอย่างง่าย ๆ :

$arr = array(0 => array(id=>1,name=>"cat 1"),
             1 => array(id=>2,name=>"cat 2"),
             2 => array(id=>3,name=>"cat 1")
);

เมื่อฉันค้นหา key = name and value = "cat 1" ฟังก์ชั่นควรกลับมา:

array(0 => array(id=>1,name=>"cat 1"),
      1 => array(id=>3,name=>"cat 1")
);

ฉันเดาว่าฟังก์ชั่นจะต้องเรียกซ้ำเพื่อลงไปสู่ระดับที่ลึกที่สุด

คำตอบ:


217

รหัส:

function search($array, $key, $value)
{
    $results = array();

    if (is_array($array)) {
        if (isset($array[$key]) && $array[$key] == $value) {
            $results[] = $array;
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, search($subarray, $key, $value));
        }
    }

    return $results;
}

$arr = array(0 => array(id=>1,name=>"cat 1"),
             1 => array(id=>2,name=>"cat 2"),
             2 => array(id=>3,name=>"cat 1"));

print_r(search($arr, 'name', 'cat 1'));

เอาท์พุท:

Array
(
    [0] => Array
        (
            [id] => 1
            [name] => cat 1
        )

    [1] => Array
        (
            [id] => 3
            [name] => cat 1
        )

)

หากประสิทธิภาพเป็นสิ่งสำคัญคุณสามารถเขียนได้ดังนั้นการเรียกซ้ำทั้งหมดจะเก็บผลลัพธ์ไว้ใน$resultsอาร์เรย์ชั่วคราวเดียวกันแทนที่จะรวมอาร์เรย์เข้าด้วยกันดังนี้:

function search($array, $key, $value)
{
    $results = array();
    search_r($array, $key, $value, $results);
    return $results;
}

function search_r($array, $key, $value, &$results)
{
    if (!is_array($array)) {
        return;
    }

    if (isset($array[$key]) && $array[$key] == $value) {
        $results[] = $array;
    }

    foreach ($array as $subarray) {
        search_r($subarray, $key, $value, $results);
    }
}

กุญแจนั่นคือที่search_rใช้พารามิเตอร์ที่สี่ของมันโดยการอ้างอิงมากกว่าโดยค่า; เครื่องหมายแอมเปอร์แซนด์&เป็นสิ่งสำคัญ

FYI: หากคุณมี PHP เวอร์ชันที่เก่ากว่าคุณต้องระบุส่วนการส่งผ่านอ้างอิงในการเรียกไปยังsearch_rไม่ใช่ในการประกาศ search_r($subarray, $key, $value, &$results)นั่นคือบรรทัดสุดท้ายจะกลายเป็น


2
@JohnKugelman ข้อผิดพลาดคำตอบ "มีประสิทธิภาพ" จะหายไปหรือ$keyไม่ถ้า ไม่มีในอาร์เรย์? จะดีกว่าif (array_key_exists($key, $array) && $array[$key] == $value) {ไหมถ้าทำ
Chase

1
@JohnKugelman ฟังก์ชั่นนี้ใช้งานได้ดี แต่บางครั้งฉันก็มีของฉัน$valueซึ่งnullฟังก์ชั่นนี้ใช้งานไม่ได้ ... array empty... วิธีการมีอาร์เรย์แม้ว่า$value= null? ชอบsearch($array, 'id', null)ไหม
Zagloo

71

แล้วรุ่นSPLล่ะ? มันจะช่วยให้คุณพิมพ์:

// I changed your input example to make it harder and
// to show it works at lower depths:

$arr = array(0 => array('id'=>1,'name'=>"cat 1"),
             1 => array(array('id'=>3,'name'=>"cat 1")),
             2 => array('id'=>2,'name'=>"cat 2")
);

//here's the code:

    $arrIt = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr));

 foreach ($arrIt as $sub) {
    $subArray = $arrIt->getSubIterator();
    if ($subArray['name'] === 'cat 1') {
        $outputArray[] = iterator_to_array($subArray);
    }
}

สิ่งที่ยอดเยี่ยมคือโดยทั่วไปแล้วรหัสเดียวกันจะวนซ้ำผ่านไดเรกทอรีสำหรับคุณโดยใช้ RecursiveDirectoryIterator แทนที่จะเป็น RecursiveArrayIterator SPL คือ roxor

คนเกียจคร้านเพียงคนเดียวเกี่ยวกับ SPL คือมันมีเอกสารไม่ดีบนเว็บ แต่หนังสือ PHP หลายเล่มมีรายละเอียดที่เป็นประโยชน์โดยเฉพาะ Pro PHP และคุณสามารถ google เพื่อรับข้อมูลเพิ่มเติมได้เช่นกัน


มันใช้งานได้อย่างมีเสน่ห์และฉันวางแผนที่จะใช้มันอีกครั้งสำหรับปัญหาที่คล้ายกัน: D ส่วนที่แปลกประหลาดอยู่ด้านหน้าและใช้ฟังก์ชัน getSubIterator บน RecursiveIteratorIterator แทนที่จะเป็นตัวแปร $ sub ฉันคิดว่ามันเป็นตัวพิมพ์ผิดในตอนแรก แต่มันก็เป็นวิธีที่ถูกต้อง! ขอบคุณเจเร็ด
bchhun

2
ทางออกที่ดีเลิศ ค่อนข้างเร็วด้วย!
TaylorOtwell

ขอบคุณสำหรับการแก้ปัญหา เราจะได้รับ "id" ที่ไหน? จาก $ outputArray
trante

ขอบคุณโซลูชันที่ตรงไปตรงมามาก แต่ไม่รู้เกี่ยวกับประสิทธิภาพหรือไม่
Mahesh.D

วิธีการยกเลิกการตั้งค่าองค์ประกอบที่พบ (อาจเป็นอาเรย์ย่อย) จากอาเรย์เดิม?
Fr0zenFyr

49
<?php
$arr = array(0 => array("id"=>1,"name"=>"cat 1"),
             1 => array("id"=>2,"name"=>"cat 2"),
             2 => array("id"=>3,"name"=>"cat 1")
);
$arr = array_filter($arr, function($ar) {
   return ($ar['name'] == 'cat 1');
   //return ($ar['name'] == 'cat 1' AND $ar['id'] == '3');// you can add multiple conditions
});

echo "<pre>";
print_r($arr);

?>

Ref: http://php.net/manual/th/function.array-filter.php


4
นี่เป็นวิธีแก้ปัญหาที่ดีถ้าคุณต้องการค้นหาอาเรย์ที่มีความลึกเพียงระดับเดียว แต่คำถามนี้เกี่ยวกับการค้นหาอาเรย์แบบวนซ้ำ ("ฟังก์ชันจะต้องเรียกซ้ำเพื่อลงไปสู่ระดับที่ลึกที่สุด")
orrd

16

กลับมาโพสต์การอัปเดตนี้สำหรับทุกคนที่ต้องการเคล็ดลับการเพิ่มประสิทธิภาพสำหรับคำตอบเหล่านี้โดยเฉพาะคำตอบที่ยอดเยี่ยมของ John Kugelman โดยเฉพาะ

ฟังก์ชั่นที่โพสต์ของเขานั้นทำงานได้ดี แต่ฉันต้องปรับสถานการณ์นี้ให้เหมาะสมเพื่อจัดการผลลัพธ์ 12,000 แถว ฟังก์ชั่นนี้ใช้เวลาชั่วนิรันดร์ 8 วินาทีในการผ่านบันทึกทั้งหมด waaaaaay ยาวเกินไป

ฉันต้องการฟังก์ชั่นเพื่อหยุดค้นหาและย้อนกลับเมื่อพบข้อมูลที่ตรงกัน นั่นคือถ้าค้นหา customer_id เรารู้ว่าเรามีเพียงหนึ่งใน resultset และเมื่อเราพบ customer_id ในอาร์เรย์หลายมิติเราต้องการกลับมา

นี่คือฟังก์ชั่นนี้ที่ได้รับการปรับปรุงความเร็ว (และง่ายมาก) สำหรับทุกคนที่ต้องการ ซึ่งแตกต่างจากรุ่นอื่น ๆ มันสามารถจัดการได้เพียงหนึ่งความลึกของอาเรย์เท่านั้นไม่คืนและไม่รวมกับผลลัพธ์หลายอย่าง

// search array for specific key = value
public function searchSubArray(Array $array, $key, $value) {   
    foreach ($array as $subarray){  
        if (isset($subarray[$key]) && $subarray[$key] == $value)
          return $subarray;       
    } 
}

สิ่งนี้นำมาซึ่งงานเพื่อให้ตรงกับ 12,000 บันทึกถึง 1.5 วินาที ยังมีค่าใช้จ่ายสูงแต่ก็สมเหตุสมผลกว่า


คนนี้จะเร็วกว่า Jhon / คำตอบของเจเร็ด (0.0009999275207519) VS (0.0020008087158203) .. ดีการทดสอบนี้เป็นเฉพาะกับกรณีและสภาพแวดล้อมของฉัน .. อิ่มผสานกับนี้ต้องขอบคุณ stefgosselin
Awena

14
if (isset($array[$key]) && $array[$key] == $value)

การเปลี่ยนแปลงเล็กน้อยในเวอร์ชันที่รวดเร็ว


2
ที่จริงแล้วสิ่งนี้จะป้องกันมิให้ส่งคำเตือนเมื่อไม่ได้ตั้งค่าคีย์ ไม่น้อยเลย! -> +1 รายการ
stefgosselin

2
ตกลงความสามารถในการมองผ่านบันทึกข้อผิดพลาด php จริงสำหรับข้อผิดพลาดที่สำคัญและไม่ได้ปนเปื้อนด้วยคำเตือนเป็นวิธีที่จะไปในความคิดของฉัน
codercake

นี่ไม่ใช่วิธีการแก้ปัญหาที่สมบูรณ์และเป็น "ความพยายามตอบสนองต่อการโพสต์อื่น" และ "ไม่ใช่คำตอบ"
mickmackusa

7

ระวังอัลกอริธึมการค้นหาแบบเชิงเส้น (ด้านบนเป็นแบบเชิงเส้น) ในอาร์เรย์แบบหลายมิติเนื่องจากมีความซับซ้อนที่เพิ่มขึ้นเมื่อความลึกเพิ่มจำนวนการวนซ้ำที่จำเป็นในการสำรวจทั้งอาร์เรย์ เช่น:

array(
    [0] => array ([0] => something, [1] => something_else))
    ...
    [100] => array ([0] => something100, [1] => something_else100))
)

จะใช้การวนซ้ำมากที่สุด 200 ครั้งเพื่อค้นหาสิ่งที่คุณกำลังมองหา (หากเข็มอยู่ที่ [100] [1]) ด้วยอัลกอริทึมที่เหมาะสม

อัลกอริธึมเชิงเส้นในกรณีนี้ทำงานที่ O (n) (เรียงลำดับจำนวนองค์ประกอบทั้งหมดในอาเรย์ทั้งหมด) ซึ่งไม่ดีมีล้านรายการ (เช่นอาร์เรย์ 1000x100x10) จะใช้เวลาเฉลี่ย 500,000 ครั้งในการค้นหาเข็ม จะเกิดอะไรขึ้นถ้าคุณตัดสินใจที่จะเปลี่ยนโครงสร้างของอาร์เรย์หลายมิติของคุณ และ PHP จะใช้อัลกอริทึมแบบเรียกซ้ำถ้าความลึกของคุณมากกว่า 100 วิทยาศาสตร์คอมพิวเตอร์สามารถทำได้ดีกว่า:

หากเป็นไปได้ให้ใช้วัตถุแทนอาร์เรย์หลายมิติเสมอ:

ArrayObject(
   MyObject(something, something_else))
   ...
   MyObject(something100, something_else100))
)

และใช้อินเทอร์เฟซตัวเปรียบเทียบแบบกำหนดเองและฟังก์ชันเพื่อจัดเรียงและค้นหา:

interface Comparable {
   public function compareTo(Comparable $o);
}

class MyObject implements Comparable {
   public function compareTo(Comparable $o){
      ...
   }
}

function myComp(Comparable $a, Comparable $b){
    return $a->compareTo($b);
}

คุณสามารถใช้uasort()เพื่อใช้เครื่องมือเปรียบเทียบแบบกำหนดเองหากคุณรู้สึกว่าการผจญภัยคุณควรใช้คอลเลกชันของคุณเองสำหรับวัตถุที่สามารถเรียงลำดับและจัดการพวกมัน (ฉันมักจะขยาย ArrayObject เพื่อรวมฟังก์ชั่นการค้นหาอย่างน้อยที่สุด)

$arrayObj->uasort("myComp");

เมื่อพวกเขาถูกจัดเรียง (uasort คือ O (n log n) ซึ่งดีเท่าที่ได้รับเหนือข้อมูลที่กำหนดเอง) การค้นหาแบบไบนารีสามารถดำเนินการในเวลา O (log n) เวลานั่นคือล้านรายการใช้เวลาประมาณ 20 ครั้งในการทำซ้ำ ค้นหา. เท่าที่ฉันทราบการค้นหาไบนารีตัวเปรียบเทียบแบบกำหนดเองไม่ได้นำมาใช้ใน PHP ( array_search()ใช้การเรียงลำดับตามธรรมชาติซึ่งทำงานกับการอ้างอิงวัตถุไม่ใช่คุณสมบัติของพวกเขา) คุณจะต้องใช้สิ่งนี้ด้วยตนเองเหมือนที่ฉันทำ

วิธีนี้มีประสิทธิภาพมากขึ้น (ไม่มีความลึกอีกต่อไป) และมีความสำคัญมากกว่า (สมมติว่าคุณบังคับใช้ความสามารถในการเปรียบเทียบโดยใช้อินเตอร์เฟส) เนื่องจากวัตถุจะกำหนดวิธีการเรียงลำดับดังนั้นคุณจึงสามารถรีไซเคิลรหัสได้อย่างไม่สิ้นสุด ดีกว่ามาก =)


คำตอบนี้ควรถูกต้อง แม้ว่าวิธีการค้นหากำลังดุร้ายจะทำ แต่ก็ใช้ทรัพยากรน้อยกว่ามาก
ดึง

ควรสังเกตว่าสิ่งที่คุณกำลังแนะนำนั้นสมเหตุสมผลถ้าคุณค้นหาอาเรย์เดียวกันหลายครั้ง มันใช้เวลานานกว่าจะผ่านปัญหาในการเรียงลำดับ (O (n log n)) มากกว่าการค้นหาเชิงเส้นสำหรับค่า (O (n)) แต่เมื่อมีการเรียงลำดับแน่นอนแล้วการค้นหาแบบไบนารีจะเร็วขึ้น
orrd

ฉันควรเพิ่มว่าการใช้วัตถุแทนอาร์เรย์อาจเป็นนามธรรมที่มีประโยชน์ แต่คุณสามารถทำการค้นหาแบบไบนารี่บนอาเรย์ถ้าอาร์เรย์ถูกเรียงลำดับ คุณไม่จำเป็นต้องใช้วัตถุในการเรียงลำดับอาร์เรย์หรือทำการค้นหาแบบไบนารีบนมัน
orrd

6

นี่คือทางออก:

<?php
$students['e1003']['birthplace'] = ("Mandaluyong <br>");
$students['ter1003']['birthplace'] = ("San Juan <br>");
$students['fgg1003']['birthplace'] = ("Quezon City <br>");
$students['bdf1003']['birthplace'] = ("Manila <br>");

$key = array_search('Delata Jona', array_column($students, 'name'));
echo $key;  

?>

5
$result = array_filter($arr, function ($var) {   
  $found = false;
  array_walk_recursive($var, function ($item, $key) use (&$found) {  
    $found = $found || $key == "name" && $item == "cat 1";
  });
  return $found;
});


3
function in_multi_array($needle, $key, $haystack) 
{
    $in_multi_array = false;
    if (in_array($needle, $haystack))
    {
        $in_multi_array = true; 
    }else 
    {
       foreach( $haystack as $key1 => $val )
       {
           if(is_array($val)) 
           {
               if($this->in_multi_array($needle, $key, $val)) 
               {
                   $in_multi_array = true;
                   break;
               }
           }
        }
    }

    return $in_multi_array;
} 

เคสของฉันแตกต่างกัน แต่ได้รับคำใบ้จากคำตอบของคุณ
shyammakwana.me

2

ฉันต้องการสิ่งที่คล้ายกัน แต่เพื่อค้นหาอาร์เรย์หลายมิติตามค่า ... ฉันใช้ตัวอย่างของ John และเขียน

function _search_array_by_value($array, $value) {
        $results = array();
        if (is_array($array)) {
            $found = array_search($value,$array);
            if ($found) {
                $results[] = $found;
            }
            foreach ($array as $subarray)
                $results = array_merge($results, $this->_search_array_by_value($subarray, $value));
        }
        return $results;
    }

ฉันหวังว่ามันจะช่วยให้ใครบางคน :)


2

นี่เป็นฟังก์ชั่นที่ได้รับการแก้ไขจากสิ่งที่ John K. โพสต์ ... ฉันต้องคว้าเฉพาะคีย์เฉพาะในอาเรย์และไม่มีอะไรข้างต้น

function search_array ( $array, $key, $value )
{
    $results = array();

    if ( is_array($array) )
    {
        if ( $array[$key] == $value )
        {
            $results[] = $array;
        } else {
            foreach ($array as $subarray) 
                $results = array_merge( $results, $this->search_array($subarray, $key, $value) );
        }
    }

    return $results;
}

$arr = array(0 => array(id=>1,name=>"cat 1"),
       1 => array(id=>2,name=>"cat 2"),
       2 => array(id=>3,name=>"cat 1"));

print_r(search_array($arr, 'name', 'cat 1'));

1

และอีกรุ่นที่ส่งกลับค่าคีย์จากองค์ประกอบอาร์เรย์ที่พบค่า (ไม่เรียกซ้ำปรับให้เหมาะสมสำหรับความเร็ว):

// if the array is 
$arr['apples'] = array('id' => 1);
$arr['oranges'] = array('id' => 2);

//then 
print_r(search_array($arr, 'id', 2);
// returns Array ( [oranges] => Array ( [id] => 2 ) ) 
// instead of Array ( [0] => Array ( [id] => 2 ) )

// search array for specific key = value
function search_array($array, $key, $value) {
  $return = array();   
  foreach ($array as $k=>$subarray){  
    if (isset($subarray[$key]) && $subarray[$key] == $value) {
      $return[$k] = $subarray;
      return $return;
    } 
  }
}

ขอบคุณทุกคนที่โพสต์ที่นี่


1
function findKey($tab, $key){
    foreach($tab as $k => $value){ 
        if($k==$key) return $value; 
        if(is_array($value)){ 
            $find = findKey($value, $key);
            if($find) return $find;
        }
    }
    return null;
}

2
คุณช่วยขยายคำตอบนี้ได้ไหม? คำตอบของรหัสเท่านั้นไม่อธิบายสิ่งที่คุณกำลังทำจริง ๆ
คนรวย Benner

โปรดอัปเดตคำถามของคุณด้วยความตั้งใจที่จะให้ความรู้
mickmackusa

ฟังก์ชั่นนี้ใช้สำหรับค้นหาคีย์เท่านั้นมันใช้งานได้สำหรับฉัน
Giovanny Gonzalez

0

หากคุณต้องการค้นหาอาเรย์ของคีย์นี่เป็นสิ่งที่ดี

function searchKeysInMultiDimensionalArray($array, $keys)
{
    $results = array();

    if (is_array($array)) {
        $resultArray = array_intersect_key($array, array_flip($keys));
        if (!empty($resultArray)) {
            $results[] = $resultArray;
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
        }
    }

    return $results;
}

คีย์จะไม่เขียนทับเพราะแต่ละชุดของคีย์ => ค่าจะอยู่ในอาร์เรย์แยกต่างหากในอาร์เรย์ผลลัพธ์
หากคุณไม่ต้องการคีย์ที่ซ้ำกันให้ใช้ปุ่มนี้

function searchKeysInMultiDimensionalArray($array, $keys)
{
    $results = array();

    if (is_array($array)) {
        $resultArray = array_intersect_key($array, array_flip($keys));
        if (!empty($resultArray)) {
            foreach($resultArray as $key => $single) {

                $results[$key] = $single;
            }
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
        }
    }

    return $results;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.