【问题标题】:Laravel flatten and pluck from multidimensional collectionLaravel 从多维集合中展平和采摘
【发布时间】:2025-12-21 23:55:12
【问题描述】:

我只需要从给定的集合中检索 id 数组,例如 [10,54,61,21,etc]。 我已经尝试过 flattenpluck,但除了 foreach 之外似乎没有任何效果,这是我想在这一步删除的东西。

// Model
class Children extends Eloquent {
    public function directChildrens(){
        return $this->hasMany('App\Children','father_id','id')->select('id','father_id');
    }

    public function childrens(){
        return $this->directChildrens()->with('childrens');
    }
}

// Controller
return $children->childrens()->get();

正如预期的那样,它工作正常。结果如下:

[{
"id": 10,
"father_id": 2,
"childrens": [
    {
        "id": 54,
        "father_id": 10,
        "childrens": [
            {
                "id": 61,
                "father_id": 54,
                "childrens": []
            }
        ]
    },
    {
        "id": 21,
        "father_id": 10,
        "childrens": []
    }
]
}]

我如何执行 pluck('id') 这个集合以获得 [10,54,61,21]

【问题讨论】:

  • “结果数组”是否有可能包含多个元素?
  • @Ersoy 是什么意思?
  • prnt.sc/srvidi 根据屏幕截图是否有可能在数组中有另一个 json 对象/元素?但我发布了一个答案,让我知道它是否适用于您的情况。

标签: arrays laravel eloquent hierarchy


【解决方案1】:
$result = [
            [
                "id" => 10,
                "father_id" => 2,
                "childrens" => [
                    [
                        "id" => 54,
                        "father_id" => 10,
                        "childrens" => [
                            [
                                "id" => 61,
                                "father_id" => 54,
                                "childrens" => []
                            ]
                        ]
                    ],
                    [
                        "id" => 21,
                        "father_id" => 10,
                        "childrens" => []
                    ]
                ]
            ]
        ];
return collect($result)
        ->map(function ($value) {
            return Arr::dot($value); // converts it to the dot notation version (added the example at the end)
        })
        ->collapse() // collapse them into a single one
        ->filter(function ($value, $key) {
            return $key === 'id' || Str::endsWith($key, '.id'); // filter first id + patterns of .id
        })
        ->values() // discard dot notation keys
        ->toArray();

Arr::dot() 方法将集合转换为以下格式,将所有内容扁平化为同一级别。

[
  {
    "id": 10,
    "father_id": 2,
    "childrens.0.id": 54,
    "childrens.0.father_id": 10,
    "childrens.0.childrens.0.id": 61,
    "childrens.0.childrens.0.father_id": 54,
    "childrens.0.childrens.0.childrens": [],
    "childrens.1.id": 21,
    "childrens.1.father_id": 10,
    "childrens.1.childrens": []
  }
]

感谢@Dan 关于已弃用/删除的函数array_dot 的警告。替换为Arr::dot()

【讨论】:

  • 我建议使用 Illuminate\Support\Arr::dot() 而不是 array_dot 助手,因为从 Laravel 5.8 开始,这些 helpers are deprecated 并最终在 Laravel 5.9 中成为 removed all together
  • 谢谢@Dan - 不知道 - 我会相应地更新它。
【解决方案2】:

您只能调用一次array_walk_recursive,我只是将其包装成pipe method 以使用 Eloquent 和 Collections 中使用的函数式方法

return $children->childrens()->get()->pipe(function ($collection) {
    $array = $collection->toArray();
    $ids = [];
    array_walk_recursive($array, function ($value, $key) use (&$ids) {
        if ($key === 'id') {
            $ids[] = $value;
        };
    });
    return $ids;
});

注意,必须将集合转换为数组并将其存储在变量中,因为array_walk_recursive第一个参数是通过引用传递的,那么下面的代码会导致致命错误只有变量应该通过引用传递

array_walk_recursive($collection->toArray(), function (){
    // ...
}) 

【讨论】:

    【解决方案3】:

    这里的递归解决方案需要声明一个额外的方法:

    function extractIds($obj)
    {
        $result = [];
    
        if (isset($obj['id'])) {
            $result[] = $obj['id'];
        }
    
        if (isset($obj['children'])) {
            return array_merge($result, ...array_map('extractIds', $obj['children']));
        }
    
        return $result;
    }
    

    该方法将在递归之前安全地检查 id 属性以及 childrens 属性。 (顺便说一句,children 这个词已经是复数形式了,不需要在末尾额外添加s。所以请注意,我在示例中删除了它。)

    使用它,我们可以利用 Laravel 集合轻松链接所需的转换调用:

    $result = collect($array)
        ->map('extractIds')
        ->flatten()
        ->toArray();
    

    【讨论】:

      【解决方案4】:

      不确定你想要pluck('id') 的意思,但 pluck 不适用于递归数组。所以我使用递归来获取 id 值。

      $result->map(function ($item) {
        $ids = [];
        array_walk_recursive($item['childrens'], function ($item, $key) use(&$ids) {
           if($key === 'id') $ids[] = $item;
        });
        return array_merge([$item['id']], $ids);
      });
      

      【讨论】:

        【解决方案5】:

        您可以使用 flatten() 来获取 [id,father_id,id,father_id,...] 的数组。 然后你做一个过滤器来获取具有偶数索引的值:

        $result = [
                    [
                        "id" => 10,
                        "father_id" => 2,
                        "childrens" => [
                            [
                                "id" => 54,
                                "father_id" => 10,
                                "childrens" => [
                                    [
                                        "id" => 61,
                                        "father_id" => 54,
                                        "childrens" => []
                                    ]
                                ]
                            ],
                            [
                                "id" => 21,
                                "father_id" => 10,
                                "childrens" => []
                            ]
                        ]
                    ]
                ];
        
        
        $collection = collect($result);
        
        $flattened = $collection->flatten()->all();
        
        return array_filter($flattened, function ($input) {return !($input & 1);}, ARRAY_FILTER_USE_KEY);
        

        如果第一个节点有一个father_id(如果是树的头),你可以添加一个检查,如果没有,你取第一个id并为孩子执行这段代码。

        【讨论】:

          【解决方案6】:

          这是一个递归算法,您可以添加到您的 Children 模型中:

          use Illuminate\Support\Collection;
          
          public function getAllChildrenIds()
          {
              return Collection::make([$this->getKey()])
                  ->merge(
                      $this->childrens->map->getAllChildrenIds()
                  );
          }
          

          【讨论】:

          • 采摘id无法处理你的多维示例
          • 那么,你为什么发布这个答案?那里没有我的任何东西,除非你提供它的真正用途。
          • `除了 foreach 之外似乎没有任何作用`我为您提供了除 foreach 之外的另一种方式,这主要是您想要达到的目标。接受与否取决于你。