我需要一些分组算法方面的帮助...我在 Laravel 集合中有一些对象,以下是 json 表示形式的一些示例数据:
[
{
"group": "WHITE",
"name": "John Doe",
"sequence": 1
},
{
"group": "WHITE",
"name": "John Doe Jr",
"sequence": 2
},
{
"group": "BLUE",
"name": "John Doe Sr",
"sequence": 3
},
{
"group": "BLUE",
"name": "John Doe Again",
"sequence": 4
},
{
"group": "RED",
"name": "Mr John Doe",
"sequence": 5
},
{
"group": "RED",
"name": "Ms Joahnna Doe",
"sequence": 6
},
{
"group": "BLUE",
"name": "Dr Johnny Doe",
"sequence": 7
},
{
"group": "RED",
"name": "Sir John Doe",
"sequence": 8
},
{
"group": "RED",
"name": "Sir John Doe Senior",
"sequence": 9
},
{
"group": "WHITE",
"name": "Ms John Doe",
"sequence": 10
}
]
我希望能够按组名称对这些对象进行分组,但保持顺序不变,并重复出现两次或多次的键,如下所示:
{
"WHITE": [
{ "name": "John Doe", "sequence": 1 },
{ "name": "John Doe Jr", "sequence": 2 }
],
"BLUE": [
{ "name": "John Doe Sr", "sequence": 3 },
{ "name": "John Doe Again", "sequence": 4 }
],
"RED": [
{ "name": "Mr John Doe", "sequence": 5 },
{ "name": "Ms Joahnna Doe", "sequence": 6 }
],
"BLUE (second group)": [
{ "name": "Dr Johnny Doe", "sequence": 7 }
],
"RED (second group)": [
{ "name": "Sir John Doe", "sequence": 8 },
{ "name": "Sir John Doe Senior", "sequence": 9 }
],
"WHITE (second group)": [
{ "name": "Ms John Doe", "sequence": 10 }
]
}
我已经找到了这样做的方法: 我迭代每个对象,查找前面的对象中序列号不重要的出现情况,并且当满足先前的条件时,我向更改此名称的对象添加一个新的
group_name
键(即添加我的分割的计数)已经发现)...据我所知,推动原始集合中的每一次出现让我使用一个简单的$collection->groupBy('group_name')
foreach ($data as $stop) {
$previous_stops_in_group = $stops
->where('group', $stop->group)
$stop->group_name = $stop->group;
$stop->splitted = false;
if ($previous_stops_in_group->count() >0) {
$last_sequence_in_group = $previous_stops_in_group->last()->sequence;
if (($stop->sequence - $last_sequence_in_group) > 1) {
$splittedroutes++;
$stop->group_name = $stop->group . $splittedroutes;
$stop->splitted = true;
}
}
$last_splitted_group = $stops
->where('splitted',true)
->where('stop_group',$stop->stop_group)
->where('direction', $stop->direction)->last();
if (!empty($last_splitted_group) && isset($last_splitted_group->group_name)) {
$stop->group_name = $last_splitted_group->group_name;
}
$stops->push($stop);
}
不幸的是,我大约一年前写了这篇糟糕的杰作,现在我不知道发生了什么,因为这个算法应该应用于数百条记录,但它不起作用。
我想找到一个更可预测的解决方案,使用表达语法,也许使用集合的本机方法,也许是
partition()
和mergeRecursive()
的组合?
有人可以给我指出一些方向吗?我真的很挣扎也很累...
提前感谢并为我感到羞耻! :)
我不知道是否可以通过集合做得更好,但这是我的方法:
$items = [
{
"group": "WHITE",
"name": "John Doe",
"sequence": 1
},
// ...
{
"group": "WHITE",
"name": "Ms John Doe",
"sequence": 10
}
];
$result = [];
foreach ($items as $item) {
if (!isset($result[$item['group']])) $result[$item['group']] = [];
$result[$item['group']][] = [
'name' => $item['item'],
'sequence' => $item['sequence'],
];
}
使用
reduce()
(或使用 ->toArray()
转换为数组并使用 PHP 的原生 array_reduce()
)来迭代集合中的对象。使用一些 static
变量来帮助识别分组值的变化并增加每个组的计数器。
代码:(PHPize演示)
var_export(
$collection->reduce(
function ($result, $obj) {
static $previousGroup,
$groupKey,
$keyCounters = [];
$group = $obj->group;
unset($obj->group);
$keyCounters[$group] ??= 0;
if (!$previousGroup || $previousGroup !== "$group " . $keyCounters[$group]) {
$groupKey = "$group " . (++$keyCounters[$group]);
$previousGroup = $groupKey;
}
$result[$groupKey][] = $obj;
return $result;
}
)
);
输出:
array (
'WHITE 1' =>
array (
0 =>
(object) array(
'name' => 'John Doe',
'sequence' => 1,
),
1 =>
(object) array(
'name' => 'John Doe Jr',
'sequence' => 2,
),
),
'BLUE 1' =>
array (
0 =>
(object) array(
'name' => 'John Doe Sr',
'sequence' => 3,
),
1 =>
(object) array(
'name' => 'John Doe Again',
'sequence' => 4,
),
),
'RED 1' =>
array (
0 =>
(object) array(
'name' => 'Mr John Doe',
'sequence' => 5,
),
1 =>
(object) array(
'name' => 'Ms Joahnna Doe',
'sequence' => 6,
),
),
'BLUE 2' =>
array (
0 =>
(object) array(
'name' => 'Dr Johnny Doe',
'sequence' => 7,
),
),
'RED 2' =>
array (
0 =>
(object) array(
'name' => 'Sir John Doe',
'sequence' => 8,
),
1 =>
(object) array(
'name' => 'Sir John Doe Senior',
'sequence' => 9,
),
),
'WHITE 2' =>
array (
0 =>
(object) array(
'name' => 'Ms John Doe',
'sequence' => 10,
),
),
)