通过属性搜索数组中对象的有效方法

时间:2011-08-18 11:36:36

标签: php arrays

好吧,有类似的东西:

$array[0]->id = 'one';
$array[0]->color = 'white';
$array[1]->id = 'two';
$array[1]->color = 'red';
$array[2]->id = 'three';
$array[2]->color = 'blue';

实现像

这样的方法的最快,最有效的方法是什么

function findObjectById($ id){}

如果我调用它将返回对象$ array [0]:

$obj = findObjectById('one')

并且如果我通过'四'作为参数将返回false。

9 个答案:

答案 0 :(得分:8)

您可以迭代这些对象:

function findObjectById($id){
    $array = array( /* your array of objects */ );

    foreach ( $array as $element ) {
        if ( $id == $element->id ) {
            return $element;
        }
    }

    return false;
}

修改

更快的方法是让一个数组的键等于对象的id(如果是唯一的);

然后你可以建立你的功能如下:

function findObjectById($id){
    $array = array( /* your array of objects with ids as keys */ );

    if ( isset( $array[$id] ) ) {
        return $array[$id];
    }

    return false;
}

答案 1 :(得分:3)

好吧,你将不得不循环遍历它们并检查比较ID,除非您的数组已经排序(按ID),在这种情况下,您可以实现搜索算法,如二进制搜索或类似的东西,以使其更快。< / p>

我的建议是首先使用排序算法(二进制排序,插入排序或快速排序)对数组进行排序(如果数组尚未排序)。然后你可以实现一个搜索算法,它应该可以提高性能,我认为这样做会很好。

http://www.algolist.net/Algorithms/Binary_search

答案 2 :(得分:3)

这是一个老问题,但对于规范参考,因为它在纯形式中缺失:

uniquePaths

$obj = array_column($array, null, 'id')['one'] ?? false; 是根据问题要求返回 false。它代表不匹配的值,例如例如,您可以将其设为 false 作为替代建议。

从 PHP 7.0 开始就透明了。如果您(仍然)有旧版本,则可以使用它的用户空间实现来替代。

然而 null 也意味着复制整个数组。这可能不需要。

相反,它可以用于索引数组,然后用 array_column 映射:

array_flip

在索引上,搜索问题可能仍然相同,地图只提供原始数组中的索引,因此存在参考系统。

请记住,这可能不是必需的,因为 PHP 具有写时复制功能。因此,可能会减少有意认为的重复。所以这是为了显示一些选项。


另一个选择是遍历整个数组,除非已经找到对象,否则检查匹配。一种方法是使用 $index = array_column($array, 'id'); $map = array_flip($index); $obj = $array[$map['onex'] ?? null] ?? false; :

array_reduce

此变体再次具有不匹配的返回 $obj = array_reduce($array, static function ($carry, $item) { return $carry === false && $item->id === 'one' ? $item : $carry; }, false); 要求。

false 更直接:

null

然后可以使用 $obj = array_reduce($array, static function ($carry, $item) { return $carry ?? ($item->id === 'one' ? $item : $carry); }, null); 添加不同的不匹配要求。

在自己的函数内完全暴露于 $obj = ...) ?? false; 甚至有利于直接退出匹配:

foreach

这实际上是 original answer by hsz,它显示了它的应用范围。

答案 3 :(得分:2)

具有数据结构性能的东西不仅仅是如何获得,而且主要是如何存储我的数据。

如果您可以自由设计数组,请使用关联数组:

$array['one']->id = 'one';
$array['one']->color = 'white';
$array['two']->id = 'two';
$array['two']->color = 'red';
$array['three']->id = 'three';
$array['three']->color = 'blue';

然后找到最便宜的:$ one = $ array ['one];

<强>更新

如果无法修改数组构造,可以创建一个单独的数组,将数据映射到索引。以这种方式查找对象不会花费任何时间:

$map['one'] = 0;
$map['two'] = 1;
$map['three'] = 2;
...

getObjectById()然后首先查找原始数组中id的索引,然后返回正确的对象:

$index = $map[$id];
return $array[$index];

答案 4 :(得分:2)

这是我绝对喜欢的算法,可以非常快速地在非常大的数组中快速找到所需的内容。这是我在PHP代码中创建并广泛使用的Binary Search Algorithm实现。它毫不费力地击败了直接迭代的搜索例程。您可以根据需要进行多种选择,但基本算法保持不变。

要使用此变量(此变体),必须按要查找的索引对数组进行从最低到最高的排序。

function quick_find(&$array, $property, $value_to_find, &$first_index) {
    $l = 0;
    $r = count($array) - 1;
    $m = 0;
    while ($l <= $r) {
        $m = floor(($l + $r) / 2);
        if ($array[$m]->{$property} < $value_to_find) {
            $l = $m + 1;
        } else if ($array[$m]->{$property} > $value_to_find) {
            $r = $m - 1;
        } else {
            $first_index = $m;
            return $array[$m];
        }
    }
    return FALSE;
}

并进行测试:

/* Define a class to put into our array of objects */
class test_object {
    public $index;
    public $whatever_you_want;
    public function __construct( $index_to_assign ) {
        $this->index = $index_to_assign;
        $this->whatever_you_want = rand(1, 10000000);
    }
}

/* Initialize an empty array we will fill with our objects */
$my_array = array();

/* Get a random starting index to simulate data (possibly loaded from a database) */
$my_index = rand(1256, 30000);

/* Say we are needing to locate the record with this index */
$index_to_locate = $my_index + rand(200, 30234);

/* 
 * Fill "$my_array()" with ONE MILLION objects of type "test_object" 
 * 
 * 1,000,000 objects may take a little bit to generate.  If you don't
 * feel patient, you may lower the number!
 * 
 */
for ($i = 0; $i < 1000000; $i++) {
    $searchable_object = new test_object($my_index); // Create the object
    array_push($my_array, $searchable_object); // Add it to the "$my_array" array
    $my_index++; /* Increment our unique index */
}

echo "Searching array of ".count($my_array)." objects for index: " . $index_to_locate ."\n\n";

$index_found = -1; // Variable into which the array-index at which our object was found will be placed upon return of the function.

$object = quick_find($my_array, "index", $index_to_locate, $index_found);

if ($object == NULL) {
    echo "Index $index_to_locate was not contained in the array.\n";
} else {
    echo "Object found at index $index_found!\n";
    print_r($object);
}
echo "\n\n";

现在,一些注意事项:

MAY 使用它来查找非唯一索引;数组仍必须按升序排序。然后,当找到与您的条件匹配的元素时,您必须向后移动数组以找到第一个元素,或者向前走以找到最后一个元素。它将为您的搜索添加一些“跃点”,但仍可能比迭代大型数组更快。

对于STRING索引,您可以将quick_find()中的算术比较(即“>”和“ <”)更改为PHP函数“ strcasecmp()”。只要确保STRING索引的排序方式相同(对于示例实现):按字母顺序和升序。

如果您想拥有一个可以搜索以 EITHER 升序 OR 降序排列的对象数组的版本:

function quick_find_a(&$array, $property, $value_to_find, &$first_index) {
    $l = 0;
    $r = count($array) - 1;
    $m = 0;
    while ($l <= $r) {
        $m = floor(($l + $r) / 2);
        if ($array[$m]->{$property} < $value_to_find) {
            $l = $m + 1;
        } else if ($array[$m]->{$property} > $value_to_find) {
            $r = $m - 1;
        } else {
            $first_index = $m;
            return $array[$m];
        }
    }
    return FALSE;
}

function quick_find_d(&$array, $property, $value_to_find, &$first_index) {
    $l = 0;
    $r = count($array) - 1;
    $m = 0;
    while ($l <= $r) {
        $m = floor(($l + $r) / 2);
        if ($value_to_find > $array[$m]->{$property}) {
            $r = $m - 1;
        } else if ($value_to_find < $array[$m]->{$property}) {
            $l = $m + 1;
        } else {
            $first_index = $m;
            return $array[$m];
        }
    }
    return FALSE;
}


function quick_find(&$array, $property, $value_to_find, &$first_index) {
    if ($array[0]->{$property} < $array[count($array)-1]->{$property}) {
        return quick_find_a($array, $property, $value_to_find, $first_index);
    } else {
        return quick_find_d($array, $property, $value_to_find, $first_index);
    }
}

答案 5 :(得分:1)

我喜欢在这些情况下做的事情是创建一个引用数组,从而避免重新复制对象,但有权使用对象本身的引用。

$array['one']->id = 'one';
$array['one']->color = 'white';
$array['two']->id = 'two';
$array['two']->color = 'red';
$array['three']->id = 'three';
$array['three']->color = 'blue';

然后我们可以创建一个简单的引用数组:

$ref = array();
foreach ( $array as $row )
    $ref[$row->id] = &$array[$row->id];

现在我们可以简单地测试数组中是否存在实例,如果需要,甚至可以像原始对象一样使用它:

if ( isset( $ref['one'] ) )
    echo $ref['one']->color;

会输出:

white

如果有问题的id不存在,则isset()将返回false,因此不需要反复遍历原始对象寻找值......我们只使用PHP的isset()函数并避免使用一个单独的功能。

请注意在使用您想要的参考时使用“&amp;”使用原始数组而不是迭代器,因此使用&amp; $ row不会给你你想要的东西。

答案 6 :(得分:1)

您可以像这样使用php的array_search函数

$key=array_search("one", array_column(json_decode(json_encode($array),TRUE), 'color'));
var_dump($array[$key]);

答案 7 :(得分:0)

这是我使用的。循环遍历对象数组的可重用函数。第二个允许您直接从所有匹配中检索单个对象(第一个匹配条件)。

function get_objects_where($match, $objects) {
    if ($match == '' || !is_array($match)) return array ();
    $wanted_objects = array ();
    foreach ($objects as $object) {
        $wanted = false;
        foreach ($match as $k => $v) {
            if (is_object($object) && isset($object->$k) && $object->$k == $v) {
                $wanted = true;
            } else {
                $wanted = false;
                break;
            };
        };
        if ($wanted) $wanted_objects[] = $object;
    };
    return $wanted_objects;
};

function get_object_where($match, $objects) {
    if ($match == '' || !is_array($match)) return (object) array ();
    $wanted_objects = get_objects_where($match, $objects);
    return count($wanted_objects) > 0 ? $wanted_objects[0] : (object) array ();
};

答案 8 :(得分:0)

这绝对没有效率,O(N)。但看起来很性感:

 $result = array_reduce($array, function ($found, $obj) use ($id) {
     return $obj['id'] == $id ? $obj : $found;
 }, null);

附录: 我看到 hakre 已经发布了类似的内容。