如何从数组中删除对象?

46

在PHP中,从对象数组中删除对象的一种优雅方法是什么?

class Data{

  private $arrObservers;

  public add(Observer $o) {  
    array_push($this->arrObservers, $o);  
  }    
  public remove(Observer $o) {  
    // I NEED THIS CODE to remove $o from $this->arrObservers
  }  
  
}
12个回答

64

你可以这样做

function unsetValue(array $array, $value, $strict = TRUE)
{
    if(($key = array_search($value, $array, $strict)) !== FALSE) {
        unset($array[$key]);
    }
    return $array;
}

你也可以使用spl_object_hash为对象创建哈希并将其用作数组键。

然而,PHP还有一个原生的数据结构,用于对象集合: SplObjectStorage

$a = new StdClass; $a->id = 1;
$b = new StdClass; $b->id = 2;
$c = new StdClass; $c->id = 3;

$storage = new SplObjectStorage;
$storage->attach($a);
$storage->attach($b);
$storage->attach($c);
echo $storage->count(); // 3

// trying to attach same object again
$storage->attach($c);
echo $storage->count(); // still 3

var_dump( $storage->contains($b) ); // TRUE
$storage->detach($b);
var_dump( $storage->contains($b) ); // FALSE

SplObjectStorage可遍历的(Traversable),因此您也可以使用foreach命令来遍历它。

另外一点,PHP还原生支持SubjectObserver接口。


4
+1 这是最佳方法。更不用说自 PHP 5.3 起,SplObjectStorage 实现了 ArrayAccess - Ionuț G. Stan
考虑到问题的性质,如果 search_array() 返回 false,最好的做法是 throw new OutOfBoundsException("No matching Observer found attached to the subject.")。仔细考虑,附加的 Observer 不再存在将是一个异常情况! :-) - Anthony Rutledge

22

我同意上面的答案,但为了完整起见(当您没有唯一ID可用作键时),我删除数组中值的首选方法如下:

/**
 * Remove each instance of a value within an array
 * @param array $array
 * @param mixed $value
 * @return array
 */
function array_remove(&$array, $value)
{
    return array_filter($array, function($a) use($value) {
        return $a !== $value;
    });
}

/**
 * Remove each instance of an object within an array (matched on a given property, $prop)
 * @param array $array
 * @param mixed $value
 * @param string $prop
 * @return array
 */
function array_remove_object(&$array, $value, $prop)
{
    return array_filter($array, function($a) use($value, $prop) {
        return $a->$prop !== $value;
    });
}

以下是使用方式:

$values = array(
    1, 2, 5, 3, 5, 6, 7, 1, 2, 4, 5, 6, 6, 8, 8,
);
print_r(array_remove($values, 6));

class Obj {
    public $id;
    public function __construct($id) {
        $this->id = $id;
    }
}
$objects = array(
    new Obj(1), new Obj(2), new Obj(4), new Obj(3), new Obj(6), new Obj(4), new Obj(3), new Obj(1), new Obj(5),
);
print_r(array_remove_object($objects, 1, 'id'));

8

我建议使用对象的ID(如果有的话,任何对该对象唯一性的标识都可以)作为数组键。这样,您就可以在不必运行循环或将ID存储在另一个位置的情况下在数组中引用该对象。代码应该如下所示:

$obj_array[$obj1->getId()] = $obj1;
$obj_array[$obj2->getId()] = $obj2;
$obj_array[$obj3->getId()] = $obj3;

unset($obj_array[$object_id]);

更新:

class Data{

  private $arrObservers;

  public add(Observer $o) {  
    $this->arrObservers[$o->getId()] = $o;  
  }    
  public remove(Observer $o) {  
    unset($this->arrObservers[$o->getId()]);
  }  

}

2
你需要为观察者对象编写一个 getId() 方法。 - Cody Snider

5

unset($myArray[$index]);中的$index是您要删除的元素的索引。如果您想获得更具体的答案,请展示一些代码或描述您正在尝试做什么。


这个可以,但是我怎么找到一个对象的索引?按数组中的值搜索? - johnlemon
@daniphp 你可以使用 === 来比较对象。 - Richard Knop

4
$obj_array['obj1'] = $obj1;
$obj_array['obj2'] = $obj2;
$obj_array['obj3'] = $obj3;
unset($obj_array['obj3']);

2
-1 是因为 OP 给了函数一个对象,而不是键,因此这个例子是不完整的。 - Mikulas Dite

1

要从多维数组中删除一个对象,您可以使用以下代码:

$exampleArray= [
    [
      "myKey"=>"This is my key",
      "myValue"=>"10"
    ],
    [
      "myKey"=>"Oh!",
      "myValue"=>"11"
    ]
];

使用array_column函数,您可以指定键列名:
if(($key = array_search("Oh!", array_column($exampleArray, 'myKey'))) !== false) {
    unset($exampleArray[$key]);
}

这将会移除指定的对象。

0
如果您想从对象数组中删除一个或多个对象(使用spl_object_hash确定对象是否相同),则可以使用此方法:
$this->arrObservers = Arr::diffObjects($this->arrObservers, [$o]);

来自这个库


0

在阅读GoF书中的观察者模式部分吗?这里有一个解决方案,可以消除查找要删除的对象索引的昂贵操作。

public function addObserver(string $aspect, string $viewIndex, Observer $view)
{   
    $this->observers[$aspect][$viewIndex] = $view;
}

public function removeObserver(string $aspect, string $viewIndex)
{
    if (!isset($this->observers[$aspect])) {
        throw new OutOfBoundsException("No such aspect ({$aspect}) of this Model exists: " . __CLASS__);
    }
    
    if (!isset($this->observers[$aspect][$viewIndex])) {
        throw new OutOfBoundsException("No such View for ({$viewIndex}) was added to the aspect ({$aspect}) of this Model:" . __CLASS__);
    }

    unset($this->observers[$aspect][$viewIndex]);
}

如果您不使用“方面”维度来跟踪特定“模型”更新的哪些“视图”,则可以省略“方面”维度。
public function addObserver(string $viewIndex, Observer $view)
{   
    $this->observers[$viewIndex] = $view;
}

public function removeObserver(string $viewIndex)
{   
    if (!isset($this->observers[$viewIndex])) {
        throw new OutOfBoundsException("No such View for ({$viewIndex}) was added to this Model:" . __CLASS__);
    }

    unset($this->observers[$viewIndex]);
}

摘要

建立一种方法,在将对象分配给数组之前找到元素。否则,您将不得不先发现对象元素的索引。

如果您有大量的对象元素(甚至超过一把手),那么您可能需要首先查找对象的索引。 PHP函数array_search()是一种从值开始并返回索引/键的方法。

https://www.php.net/manual/en/function.array-search.php

在调用函数时,请务必使用 strict 参数。

如果将第三个参数 strict 设置为 true,则 array_search() 函数将在 haystack 中搜索相同的元素。这意味着它还将对 haystack 中的 needle 进行严格的类型比较,并且对象必须是相同的实例


0
 function obj_array_clean ($array, $objId)
 {
    $new = array() ;
    foreach($array as $value)
    {
        $new[$value->{$objId}] = $value;
    }
    $array = array_values($new);
    return $array;
 }

 $ext2 = obj_array_clean($ext, 'OnjId');
  • 它将从数组对象 $array 中移除重复的 object "OnjId"。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接