如何对类似于链表的条目进行排序?

时间:2016-06-24 09:43:41

标签: php arrays sorting linked-list

我有以下数据结构,我想根据beforeafter值对此进行排序。

array (size=5)
  0 => 
    array (size=3)
      'id' => int 14
      'parentId' => int 0
      'before' => int 15
  1 => 
    array (size=3)
      'id' => int 15
      'parentId' => int 0
      'after' => int 14
  2 => 
    array (size=3)
      'id' => int 9
      'parentId' => int 0
      'after' => int 15
  3 => 
    array (size=3)
      'id' => int 8
      'parentId' => int 0
      'after' => int 9
  4 => 
    array (size=3)
      'id' => int 1
      'parentId' => int 0
      'after' => int 14

有没有巧妙的方法用PHP做到这一点?

2 个答案:

答案 0 :(得分:0)

我认为这不是一个直截了当的"单线"解决这个问题。

我不清楚parentId是否对排序有任何意义,但如果没有,这与我本周早些时候回答的this question about sorting an array of Javascript includes by dependency非常相似。

唯一真正的区别在于,在依靠依赖性排序之前,您需要在"之前转换那些"进入"""相应行上的条目。

$data = [
    ["id" => 14, "parentId" => 0, "before" => 15],
    ["id" => 15, "parentId" => 0, "after" => 14],
    ["id" => 9,  "parentId" => 0, "after" => 15],
    ["id" => 8,  "parentId" => 0, "after" => 9],
    ["id" => 1,  "parentId" => 0, "after" => 14]
];

// Use the ID of each element as the array index.
$data = array_combine(array_column($data, "id"), $data);
// Convert each "after" entry into an array.
$data = array_map(function($element) {
        $element["after"] = isset($element["after"]) ? [$element["after"]] : [];
        return $element;
    }, $data);
// Convert each "before" entry into an "after" entry.
foreach ($data as $id => $element) {
    if (isset($element["before"])) {
        $data[$element["before"]]["after"][] = $id;
        unset($data[$id]["before"]);
    }
}
// Remove empty "after" entries.
$data = array_map(function($element) {
        if (!count($element["after"])) {
            unset($element["after"]);
        }
        return $element;
    }, $data);

$sorted = [];
while ($count = count($data)) {
    // Remove any met dependencies.
    foreach ($data as $id => $element) {
        if (isset($element["after"])) {
            foreach ($element["after"] as $after_id => $after_element) {
                if (isset($sorted[$after_element])) {
                    unset($data[$id]["after"][$after_id]);
                }
            }
            if (!count($data[$id]["after"])) {
                unset($data[$id]["after"]);
            }
        }
    }
    // Add elements with no more dependencies to the output array.
    foreach ($data as $id => $element) {
        if (!isset($element["after"])) {
            $sorted[$id] = $element;
            unset($data[$id]);
        }
    }
    if (count($data) == $count) {
        die("Unresolvable dependency");
    }
}
var_dump($sorted);
/*
array (size=5)
  14 => 
    array (size=2)
      'id' => int 14
      'parentId' => int 0
  15 => 
    array (size=2)
      'id' => int 15
      'parentId' => int 0
  1 => 
    array (size=2)
      'id' => int 1
      'parentId' => int 0
  9 => 
    array (size=2)
      'id' => int 9
      'parentId' => int 0
  8 => 
    array (size=2)
      'id' => int 8
      'parentId' => int 0
 */

答案 1 :(得分:0)

我认为您可以根据自己的需要使用uasort()功能。 要定义比较函数,我建议将所有after条件转换为before条件:

$array = [
    [
        'id'       => 14,
        'parentId' => 0,
        'before'   => 15
    ],
    [
        'id'       => 15,
        'parentId' => 0,
        'after'    => 14
    ],
    [
        'id'       => 9,
        'parentId' => 0,
        'after'    => 15
    ],
    [
        'id'       => 8,
        'parentId' => 0,
        'after'    => 9
    ],

    [
        'id'       => 1,
        'parentId' => 0,
        'after'    => 14
    ]
];

//transform all after conditions to before conditions
function defineBeforeCondition($array)
{
    $befores = array_column($array, 'before', 'id');
    $afters  = array_column($array, 'after', 'id');
    return array_merge(array_chunk($befores, 1, true), array_map('array_flip', array_chunk($afters, 1, true)));
}

$condition = defineBeforeCondition($array);

现在您可以在比较函数中使用$condition

$compare = function ($array1, $array2) use ($condition)
{
    //iterate through before conditions
    foreach ($condition as $before) {
        //if there is a match
        if (isset($before[$array1['id']]) && $before[$array1['id']] === $array2['id']) {
            //if the value of the first element is greater than the value of the second element,
            //but the first element should precede the second, return -1
            if ($array1['id'] > $array2['id']) {
                return -1;
            }
            //otherwise make a normal comparison
            //note the spaceship operator for PHP >= 7.0
            return $array1['id'] <=> $array2['id'];
        }
    }
    //no data, move down the first element
    return 1;
};

uasort($array, $compare);
var_dump($array);

array (size=5)
  0 => 
    array (size=3)
      'id' => int 14
      'parentId' => int 0
      'before' => int 15
  4 => 
    array (size=3)
      'id' => int 1
      'parentId' => int 0
      'after' => int 14
  1 => 
    array (size=3)
      'id' => int 15
      'parentId' => int 0
      'after' => int 14
  2 => 
    array (size=3)
      'id' => int 9
      'parentId' => int 0
      'after' => int 15
  3 => 
    array (size=3)
      'id' => int 8
      'parentId' => int 0
      'after' => int 9