【问题标题】:How to join 2 arrays of objects on the same property?如何在同一属性上连接 2 个对象数组?
【发布时间】:2017-09-26 21:51:05
【问题描述】:

假设我有这 2 个对象数组:

答:

[
  [0] => (stdClass) {
    id => '0',
    ....
  },
  [1] => (stdClass) {
    id => '1',
    ....
  },
  ,
  [2] => (stdClass) {
    id => '2',
    ....
  }
]

乙:

[
  [0] => (stdClass) {
    id => '0',
    name => 'Hello',
    a_id => '2'
    ....
  },
  [1] => (stdClass) {
    id => '1',
    name => 'World',
    a_id => '2'
    ....
  },
  [2] => (stdClass) {
    id => '2',
    name => 'foo',
    a_id => '0'
    ....
  }
]

当 B.a_id = A.id 时,来自 B 的对象属于 A,我想将数组中的对象添加到 A,如下所示:

答:

[
  ...,
  [2] => (stdClass) {
    id => '2',
    mapped_objs => [
       [0] => (stdClass) {
       id => '0',
       name => 'Hello',
       a_id => '2'
       ....
       },
      [1] => (stdClass) {
        id => '1',
        name => 'World',
        a_id => '2'
        ....
      },
    ]
  }
  ...,
]

PHP中是否有有效的算法或函数可以解决这个问题?我必须在 O(n^2) 中执行此操作吗?

【问题讨论】:

  • 如果您确保您的顶级数组键与类的 id 属性匹配,则按键查找地图将非常快,您无需通过 A 搜索。您只需通过 B 搜索并分配if (isset(array1[B.id])
  • 这里的重要问题是:id 值(属性)是否总是与键匹配?

标签: php


【解决方案1】:

O(n^2)?视情况而定

如果此结构(和 stdClass 定义)超出您的控制范围,可能。在这种情况下,唯一的选择是在A 无论何时 访问该属性时查找B.a_id。此外,当需要 A 的列表时,使用 array_filter 匹配来自 B 的集合。 (大量代码重复)如果您只需要这种关系的一侧,并且/或者不会访问这些关系的所有实例,这种解决方案只会比 O(n^2) 更有效关系。

该解决方案的一个重要组成部分是“每当访问该属性时”部分,它适用于一对多数据关系的双方。这让我找到了一个(正确的?更好的解决方案:OOP。

将对象定义为对象,尤其是用于 API。

通常当我听到开发人员说“我无法更改对象或数据结构”时,他们的真正意思是他们不想这样做。 (或者他们不知道这很容易做到)。

不要让这些对象被定义为stdClass,而是让它们成为一个真正的类。

循环遍历每个集合一次,并将它们转换为它们自己的对象,将解决您的问题没有嵌套循环,以及您尚未解决的其他问题想到。 没有嵌套循环解决这个问题的关键在于正确使用主键A.id <-- B.a_id。如果您使用A.id 作为A 对象数组的索引,则无需循环即可找到B 的匹配项。

除了一点点数据结构基础知识。如果您使用 OOP,它将挽救您的生命,所以这将是我提供的代码示例。

$arrayA = []; // your source for A
$arrayB = []; // your source for B

class A {
    public $id, $bList;

    public function __construct(stdClass $aOrig)
    {
        $this->id = $aOrig->id;
    }
    
    public function addB(B $b) {
        $this->bList[$b->id] = $b;
    }
}

class B {
    public $id, $name, $a_id, $a;
    
    public function __construct(stdClass $bOrig)
    {
        $this->id = $bOrig->id;
        $this->name = $bOrig->name;
        $this->a_id = $bOrig->a_id;
    }
}

$aList = [];
foreach ($arrayA as $aO) {
    // since .id is your data's primary-key, use it like one and make it your array-index
    $aList[$aO->id] = new A($aO); 
}

$bList = []; // not necessary, as you'll these objects in A->bList 
foreach ($arrayB as $bO) {
    // same primary-key trick (can skip creating $bList)
    $b = $bList[$bO->id] = new B($bO);
    // get A by its key (and array-index) AND assign it to B
    $b->a = $a = $aList[$b->a_id];
    // add B to A's bList and you're done.
    $a->addB($b);
}

【讨论】:

  • 设置B的父关系。A:B是一对多的关系。 A::bList = [B,B,B] 是该关系的一侧,而 B::a = A 是另一侧。它将对 B 的父 A(匹配 a_id)的引用存储在 B 的 a 属性中。然后,对于任何 B,您可以 $b->a->bList 或 A 的任何其他属性或方法。
  • 更具体地说,使用 OOP,您将拥有仅接受 B 对象的 other 对象/函数。 . . function getLongDescr(B $b): string { return $b->a->name . $b->name; }
猜你喜欢
  • 1970-01-01
  • 2016-07-18
  • 2019-07-16
  • 2019-05-30
  • 2017-07-27
  • 2016-06-03
  • 2019-02-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多