分组算法(支持 PHP 和 Laravel Collection)

问题描述 投票:0回答:2

我需要一些分组算法方面的帮助...我在 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()
的组合?

有人可以给我指出一些方向吗?我真的很挣扎也很累...

提前感谢并为我感到羞耻! :)

php laravel algorithm grouping
2个回答
0
投票

我不知道是否可以通过集合做得更好,但这是我的方法:

$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'],
    ];
}

0
投票

使用

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,
    ),
  ),
)
© www.soinside.com 2019 - 2024. All rights reserved.