在 PHP 中的线程中对消息进行分组
Group messages in threads in PHP
我有一组消息,其中包含对 parent 条消息的引用,它看起来像这样:
array (
'm1' => array ('m9'),
'm5' => array ('m3', 'm4', 'm2'),
'm2' => array ('m1'),
'm3' => array ('m2', 'm1', 'm8'),
'm6' => array ('m7'),
'm4' => array ('m3', 'm2'),
)
此数组的键是消息 ID,值是对 零个或多个 parent ID 的引用(以任何顺序)。 ID 的顺序可以是随机的,并且不能保证引用的 parent ID 在给定的消息集中。
我需要做的是将这些消息分组在 'threaded view' 中。所以基本上我需要将这个数组转换成这样的东西:
array(
'm1' => array('m1', 'm2', 'm3', 'm4', 'm5'),
'm6' => array('m6')
);
每条消息都应分配给按 top-level 消息分组的线程。当消息没有引用 parent 或引用的 parent 在集合中不存在时,消息被认为是 top-level。
消息 'm1' 和 'm6' 是 top-level 因为 'm9' 和 'm7' 不在给定的集合中。消息 'm3' 在 'm1' 线程中,尽管引用了 non-existing 'm8' - 它有其他现有的 parent 链接到 'm1'。
我的问题是如何做,以及如何高效地做?任何帮助将不胜感激。
更新:
我想到的是先把这些关系倒过来,所以就变成了:
array (
'm9' => array ('m1'), # this would be rejected
'm3' => array ('m5', 'm4'),
'm4' => array ('m5'),
'm2' => array ('m5', 'm3', 'm4'),
'm1' => array ('m2', 'm3'),
'm8' => array ('m3'), # this would be rejected
'm7' => array ('m6'), # this would be rejected
)
然后我会添加键 'm6' 和 'm5' 没有 children 因为它们存在于输入键中但不存在于转换后的数组中。
现在我有了可以在输入数据中找到的所有关系 parent => children。将此数组的键与输入数组进行比较后,我可以将键 'm9'、'm8' 和 'm7' 拒绝为 non-existing。
最后数组看起来像这样:
array (
'm3' => array ('m5', 'm4'),
'm4' => array ('m5'),
'm2' => array ('m5', 'm3', 'm4'),
'm1' => array ('m2', 'm3'),
'm6' => array(),
'm5' => array()
)
我现在需要做的是以某种方式扁平化这个结构。我需要找到每个 parent p1 也是另一个 parent p2 的 child 并追加p1children 到 p2children。
除了一遍又一遍地迭代这些数组,我不知道如何用另一种方式来做到这一点,但这不是一个选项。
这对我来说似乎是一个有趣的挑战。到目前为止我取得的成就:
首先,一个人可能会摆脱孤儿:
$a = array (
'm1' => array ('m9'),
'm5' => array ('m3', 'm4', 'm2'),
'm2' => array ('m1'),
'm3' => array ('m2', 'm1', 'm8'),
'm6' => array ('m7'),
'm4' => array ('m3', 'm2'),
);
$f = array_map(function($v) use (&$a) {
$k = key($a); next($a);
$vals = array_filter($v, function($el) use ($a) {
return isset($a[$el]);
});
return empty($vals) ? [$k] : $vals;
}, $a);
后者给出了一个数组映射 parents 到 arrays of children.
假设您手头有您最喜欢的 array_flatten
函数:
function array_flatten($array, $return) {
for($x = 0; $x <= count($array); $x++) {
if(isset($array[$x]) && is_array($array[$x])) {
$return = array_flatten($array[$x], $return);
} else {
if(isset($array[$x])) {
$return[] = $array[$x];
}
}
}
return $return;
}
现在我们可以使用下面的函数来爬树了:
function resolve_parents(&$f) {
array_walk($f, function(&$v, $k) use(&$f) {
if(!is_array($v)) { // great! that’s what we needed
$f[$k] = $v;
} else if(count($v) > 1) { // many children left
$f[$k] = array_unique(
array_flatten(
array_map(function($v) use(&$f) {
return $f[$v];
}, $v),
array())
);
} else { // great, one child left, store it as result
$f[$k] = $v[0];
};
});
}
好吧,它给了我们更上一层楼的决心。根据需要多次 运行(检查是否没有 array
作为值 ⇒ 一切都已解决):
function check_result($arr) {
return array_reduce($arr, function($memo, $v) {
return $memo = $memo && !is_array($v); }, true);
}
while(!check_result($f)) resolve_parents($f);
我们最终会生成一个数组:
// Array
// (
// [m1] => m1
// [m5] => m1
// [m2] => m1
// [m3] => m1
// [m6] => m6
// [m4] => m1
// )
这显然是您问题的答案。
不幸的是,上面的方式既不优雅也被证明是有效的。我只是把它留在这里以防代码可能会提示你。
如果您有任何问题,请随时提出。
我有一组消息,其中包含对 parent 条消息的引用,它看起来像这样:
array (
'm1' => array ('m9'),
'm5' => array ('m3', 'm4', 'm2'),
'm2' => array ('m1'),
'm3' => array ('m2', 'm1', 'm8'),
'm6' => array ('m7'),
'm4' => array ('m3', 'm2'),
)
此数组的键是消息 ID,值是对 零个或多个 parent ID 的引用(以任何顺序)。 ID 的顺序可以是随机的,并且不能保证引用的 parent ID 在给定的消息集中。
我需要做的是将这些消息分组在 'threaded view' 中。所以基本上我需要将这个数组转换成这样的东西:
array(
'm1' => array('m1', 'm2', 'm3', 'm4', 'm5'),
'm6' => array('m6')
);
每条消息都应分配给按 top-level 消息分组的线程。当消息没有引用 parent 或引用的 parent 在集合中不存在时,消息被认为是 top-level。 消息 'm1' 和 'm6' 是 top-level 因为 'm9' 和 'm7' 不在给定的集合中。消息 'm3' 在 'm1' 线程中,尽管引用了 non-existing 'm8' - 它有其他现有的 parent 链接到 'm1'。
我的问题是如何做,以及如何高效地做?任何帮助将不胜感激。
更新:
我想到的是先把这些关系倒过来,所以就变成了:
array (
'm9' => array ('m1'), # this would be rejected
'm3' => array ('m5', 'm4'),
'm4' => array ('m5'),
'm2' => array ('m5', 'm3', 'm4'),
'm1' => array ('m2', 'm3'),
'm8' => array ('m3'), # this would be rejected
'm7' => array ('m6'), # this would be rejected
)
然后我会添加键 'm6' 和 'm5' 没有 children 因为它们存在于输入键中但不存在于转换后的数组中。
现在我有了可以在输入数据中找到的所有关系 parent => children。将此数组的键与输入数组进行比较后,我可以将键 'm9'、'm8' 和 'm7' 拒绝为 non-existing。
最后数组看起来像这样:
array (
'm3' => array ('m5', 'm4'),
'm4' => array ('m5'),
'm2' => array ('m5', 'm3', 'm4'),
'm1' => array ('m2', 'm3'),
'm6' => array(),
'm5' => array()
)
我现在需要做的是以某种方式扁平化这个结构。我需要找到每个 parent p1 也是另一个 parent p2 的 child 并追加p1children 到 p2children。 除了一遍又一遍地迭代这些数组,我不知道如何用另一种方式来做到这一点,但这不是一个选项。
这对我来说似乎是一个有趣的挑战。到目前为止我取得的成就:
首先,一个人可能会摆脱孤儿:
$a = array (
'm1' => array ('m9'),
'm5' => array ('m3', 'm4', 'm2'),
'm2' => array ('m1'),
'm3' => array ('m2', 'm1', 'm8'),
'm6' => array ('m7'),
'm4' => array ('m3', 'm2'),
);
$f = array_map(function($v) use (&$a) {
$k = key($a); next($a);
$vals = array_filter($v, function($el) use ($a) {
return isset($a[$el]);
});
return empty($vals) ? [$k] : $vals;
}, $a);
后者给出了一个数组映射 parents 到 arrays of children.
假设您手头有您最喜欢的 array_flatten
函数:
function array_flatten($array, $return) {
for($x = 0; $x <= count($array); $x++) {
if(isset($array[$x]) && is_array($array[$x])) {
$return = array_flatten($array[$x], $return);
} else {
if(isset($array[$x])) {
$return[] = $array[$x];
}
}
}
return $return;
}
现在我们可以使用下面的函数来爬树了:
function resolve_parents(&$f) {
array_walk($f, function(&$v, $k) use(&$f) {
if(!is_array($v)) { // great! that’s what we needed
$f[$k] = $v;
} else if(count($v) > 1) { // many children left
$f[$k] = array_unique(
array_flatten(
array_map(function($v) use(&$f) {
return $f[$v];
}, $v),
array())
);
} else { // great, one child left, store it as result
$f[$k] = $v[0];
};
});
}
好吧,它给了我们更上一层楼的决心。根据需要多次 运行(检查是否没有 array
作为值 ⇒ 一切都已解决):
function check_result($arr) {
return array_reduce($arr, function($memo, $v) {
return $memo = $memo && !is_array($v); }, true);
}
while(!check_result($f)) resolve_parents($f);
我们最终会生成一个数组:
// Array
// (
// [m1] => m1
// [m5] => m1
// [m2] => m1
// [m3] => m1
// [m6] => m6
// [m4] => m1
// )
这显然是您问题的答案。
不幸的是,上面的方式既不优雅也被证明是有效的。我只是把它留在这里以防代码可能会提示你。
如果您有任何问题,请随时提出。