【问题标题】:How to group an array into subarrays using its keys?如何使用其键将数组分组为子数组?
【发布时间】:2017-05-24 22:45:00
【问题描述】:

我希望根据其键将数组分组为子数组。

样本数组

Array
(
    [0] => Array
        (
            [a_id] => 1
            [a_name] => A1
            [b_id] => 1
            [b_name] => B1
            [c_id] => 1
            [c_name] => C1
        )

    [1] => Array
        (
            [a_id] => 1
            [a_name] => A1
            [b_id] => 1
            [b_name] => B1
            [c_id] => 2
            [c_name] => C2
        )

    [2] => Array
        (
            [a_id] => 1
            [a_name] => A1
            [b_id] => 2
            [b_name] => B2
            [c_id] => 3
            [c_name] => C3
        )

    [3] => Array
        (
            [a_id] => 2
            [a_name] => A2
            [b_id] => 3
            [b_name] => B3
            [c_id] => 4
            [c_name] => C4
        )

)

我需要将此示例数组转换为以下格式的 JSON 数组:

预期输出

[{
    "a_id": 1,
    "a_name": "A1",
    "b_list": [{
        "b_id": 1,
        "b_name": "B1",
        "c_list": [{
            "c_id": 1,
            "c_name": "C1"
        }, {
            "c_id": 2,
            "c_name": "C2"
        }]
    }, {
        "b_id": 2,
        "b_name": "B2",
        "c_list": [{
            "c_id": 3,
            "c_name": "C3"
        }]
    }]
}, {
    "a_id": 2,
    "a_name": "A2",
    "b_list": [{
        "b_id": 3,
        "b_name": "B3",
        "c_list": [{
            "c_id": 4,
            "c_name": "C4"
        }]
    }]
}]

我可以使用下面的代码按一个键进行分组。

$array = array(
array("a_id" => "1","a_name" => "A1","b_id" => "1","b_name" => "B1","c_id" => "1","c_name" => "C1"),
array("a_id" => "1","a_name" => "A1","b_id" => "1","b_name" => "B1","c_id" => "2","c_name" => "C2"),
array("a_id" => "1","a_name" => "A1","b_id" => "2","b_name" => "B2","c_id" => "3","c_name" => "C3"),
array("a_id" => "2","a_name" => "A2","b_id" => "3","b_name" => "B3","c_id" => "4","c_name" => "C4")
);
$return = array();
foreach($array as $val) {
    $return[$val["a_id"]][] = $val;
}
print_r($return);

但是我的实际场景涉及到分组到子数组中并没有奏效。

期待看看是否有优化的方式或有用的功能可以进入我预期的 JSON 响应。

注意:我在这里研究一个通用的用例。例如:a_list 代表国家,b_list 代表州,c_list 代表城市。

【问题讨论】:

  • 你能让你的输入和预期输出尽可能小吗?所以更容易理解你期望的行为?
  • 那是……相当有趣和令人费解的。 @TomasZubiri 这几乎是最小的样本。据了解,这个分组有很多细节。
  • @SurabhilSergy 是的,有几个,但现在没有时间。它意味着某种递归。几个小时后会试一试
  • @FélixGagnon-Grenier 你有机会调查这个吗?
  • @SurabhilSergy 抱歉,我很懒,你有我可以在 php 中使用来测试解决方案的数组的实际代码示例吗?现在我必须手动输入所有数据:D

标签: php arrays json optimization multidimensional-array


【解决方案1】:

这是一个非常具体的数组用例。那么这是你的解决方案。

$array = <YOUR SAMPLE ARRAY>
$output = [];
/*
 * Nesting array based on a_id, b_id
 */
foreach ($array as $item) {
    $aid = $item['a_id'];
    $bid = $item['b_id'];
    $cid = $item['c_id'];
    if(!isset($output[$aid])){
        $output[$aid] = [
            'a_id' => $item['a_id'],
            'a_name' => $item['a_name'],
            'b_list' => [
                $bid => [
                    'b_id' => $item['b_id'],
                    'b_name' => $item['b_name'],
                    'c_list' => [
                        $cid = [
                            'c_id' => $item['c_id'],
                            'c_name' => $item['c_name']
                        ]
                    ]
                ]
            ]
        ];
    } else if (!isset($output[$aid]['b_list'][$bid])){
        $output[$aid]['b_list'][$bid] =  [
            'b_id' => $item['b_id'],
            'b_name' => $item['b_name'],
            'c_list' => [
                $cid => [
                    'c_id' => $item['c_id'],
                    'c_name' => $item['c_name']
                ]
            ]
        ];
    } else if(!isset($output[$aid]['b_list'][$bid]['c_list'][$cid])) {
        $output[$aid]['b_list'][$bid]['c_list'][$cid] = [
            'c_id' => $item['c_id'],
            'c_name' => $item['c_name']
        ];
    } else {
        // Do/Dont overrider
    }
}
/*
 * Removing the associativity from the b_list and c_list
 */
function indexed($input){

    $output = [];
    foreach ($input as $key => $item) {
        if(is_array($item)){
            if($key == 'b_list' || $key == 'c_list'){
                $output[$key] = indexed($item);
            } else {
                $output[] = indexed($item);
            }
        } else {
            $output[$key] = $item;
        }
    }
    return $output;
}
$indexed = indexed($output);
print_r(json_encode($indexed, 128));

【讨论】:

    【解决方案2】:

    那里有有趣的要求。 这是我的通用解决方案,也是可扩展的。

    function transform($array, $group=[
        ['a_id','a_name','b_list'],
        ['b_id','b_name','c_list'],
        ['c_id','c_name'],
    ]){
        foreach($array as $a){
            $r = &$result;
            foreach($group as $g){
                $x = &$r[$a[$g[0]]];
                $x[$g[0]] = $a[$g[0]];
                $x[$g[1]] = $a[$g[1]];
                if(isset($g[2])) $r = &$x[$g[2]]; else break;
            }
        }
        return transformResult($result);
    }
    
    function transformResult($result){
        foreach($result as &$a)
            foreach($a as &$b)
                if(is_array($b)) $b = transformResult($b);
        return array_values($result);
    }
    

    要扩展这个解决方案,你所要做的就是修改$group参数, 直接在函数声明中或通过将适当的值作为第二个参数传递。

    使用示例:

    echo json_encode(transform($array), JSON_PRETTY_PRINT);
    

    假设您的示例中输入相同的$array,这将返回相同的输出。

    【讨论】:

      【解决方案3】:

      现在这里是在给定情况下效果最好的代码。我创造了一个类似的情况,然后详细解释了解决方案。

      情况
      订单是多页的,具体取决于所选包裹的服务天数。每个包裹的详细信息都存储在数据库中,包含以下字段:

      1. package_id(唯一字段)
      2. package_name(包的名称,例如包 A)
      3. servings_count(一天的总份数)
      4. days_served(一个月的服务天数)

      为了将每天的膳食选择和当天的服务作为订单存储在数据库中,我需要一个可以动态定义/填充的 PHP 多维数组。

      预期的输出类似于:

      Array
      (
          [Day 1] => Array
              (
                  [meal_id_1] => Unique ID //to be replaced with user selection
                  [meal_code_1] => Meal Name //to be replaced with user selection
                  [meal_type_1] => Meal //prefilled based on the selected package
                  [meal_id_2] => Not Available //to be replaced with user selection
                  [meal_code_2] => 2 //to be replaced with user selection
                  [meal_type_2] => Meal //prefilled based on the selected package
              )
      
          [Day 2] => Array
              (
                  [meal_id_1] => Unique ID //to be replaced with user selection
                  [meal_code_1] => Meal Name //to be replaced with user selection
                  [meal_type_1] => Meal //prefilled based on the selected package
                  [meal_id_2] => Not Available //to be replaced with user selection
                  [meal_code_2] => 2 //to be replaced with user selection
                  [meal_type_2] => Meal //prefilled based on the selected package
              )
      

      上述数组是根据解释的结构和份数和天数 100% 动态创建的。下面是一些解释的代码。

      首先,我们必须声明两个 PHP 数组。

      $total_meals_array = []; //Primary, Multidimension Array
      $meals_selected_array = []; //Meals Details Array to be used as primary array's key value.
      

      完成此操作后,运行 MySQL 查询以从数据库中读取包。现在根据结果,执行以下操作:

      $total_meals_array = []; //Primary, Multidimension Array
      $meals_selected_array = []; //Meals Details Array to be used as primary array's key value.
      
      if( $num_row_packages >= 1 ) {
          while($row_packages = mysqli_fetch_array ($result_packages)) {
              $package_id = $row_packages['package_id'];
              $package_name = $row_packages['package_name'];
              $servings_count = $row_packages['servings_count'];
              $days_served = $row_packages['days_served'];
      
              //this for loop is to repeat the code inside `$days_served` number of times. This will be defining our primary and main Multidimensional Array `$total_meals_array`.
              for ($y = 1; $y <= $days_served; $y++) {
                  //once inside the code, now is the time to define/populate our secondary array that will be used as primary array's key value. `$i`, which is the meal count of each day, will be added to the key name to make it easier to read it later. This will be repeated `$meals_count` times.
      
                  for ($i = 1; $i <= $meals_count; $i++) {
                      $meals_selected_array["meal_id_" . $i] = "Unique ID";
                      $meals_selected_array["meal_code_" . $i] = "Meal Name";
                      $meals_selected_array["meal_type_" . $i] = "Meal";
                  }
      
                  //once our secondary array, which will be used as the primary array's key value, is ready, we will start defining/populating our Primary Multidimensional Array with Keys Named based on `$days_served`.
                  $total_meals_array["Day " . $y] = $meals_selected_array;
              }
          }
      }
      

      就是这样!我们的动态多维数组已经准备好了,可以通过以下代码查看:

      print "<pre>";
      print_r($total_meals_array);
      print "</pre>";
      

      谢谢大家,特别是@yarwest 好心回答我的问题。

      【讨论】:

        【解决方案4】:

        这是代码,您可以将其用于从 a_ 到 y_ 深度的索引。如果你不想要它,最里面的元素是空的。在最后一个元素之前终止 for 循环,然后单独处理最后一个元素。您还可以对此代码进行一些改进。希望这会有所帮助。

         <?php
            $array = array(
            array("a_id" => "1","a_name" => "A1","b_id" => "1","b_name" => "B1","c_id" => "1","c_name" => "C1"),
            array("a_id" => "1","a_name" => "A1","b_id" => "1","b_name" => "B1","c_id" => "2","c_name" => "C2"),
            array("a_id" => "1","a_name" => "A1","b_id" => "2","b_name" => "B2","c_id" => "3","c_name" => "C3"),
            array("a_id" => "2","a_name" => "A2","b_id" => "3","b_name" => "B3","c_id" => "4","c_name" => "C4")
            );
            $arrays = array_map(function($v){return array_chunk($v, 2, true);}, $array);
            $result = [];
            foreach($arrays as $value)
            {
                $ref = &$result;
                $len = count($value);
                $index = 0;
                for(; $index < $len; $index++)
                {
                    $arr = $value[$index];
                    $char = key($arr)[0];
                    $charAdd = chr(ord($char)+1);
                    $key = $arr[$char.'_id'].$arr[$char.'_name'];
                    $listKey = $charAdd.'_list';
                    foreach($arr as $k => $v)
                    {
                        $ref[$key][$k] = $v;
                    }
                    $ref = &$ref[$key][$listKey];
                }
            }
            var_dump($result);
        

        输出:在线live demo

        ei@localhost:~$ php test.php
        array(2) {
          ["1A1"]=>
          array(3) {
            ["a_id"]=>
            string(1) "1"
            ["a_name"]=>
            string(2) "A1"
            ["b_list"]=>
            array(2) {
              ["1B1"]=>
              array(3) {
                ["b_id"]=>
                string(1) "1"
                ["b_name"]=>
                string(2) "B1"
                ["c_list"]=>
                array(2) {
                  ["1C1"]=>
                  array(3) {
                    ["c_id"]=>
                    string(1) "1"
                    ["c_name"]=>
                    string(2) "C1"
                    ["d_list"]=>
                    NULL
                  }
                  ["2C2"]=>
                  array(3) {
                    ["c_id"]=>
                    string(1) "2"
                    ["c_name"]=>
                    string(2) "C2"
                    ["d_list"]=>
                    NULL
                  }
                }
              }
              ["2B2"]=>
              array(3) {
                ["b_id"]=>
                string(1) "2"
                ["b_name"]=>
                string(2) "B2"
                ["c_list"]=>
                array(1) {
                  ["3C3"]=>
                  array(3) {
                    ["c_id"]=>
                    string(1) "3"
                    ["c_name"]=>
                    string(2) "C3"
                    ["d_list"]=>
                    NULL
                  }
                }
              }
            }
          }
          ["2A2"]=>
          array(3) {
            ["a_id"]=>
            string(1) "2"
            ["a_name"]=>
            string(2) "A2"
            ["b_list"]=>
            array(1) {
              ["3B3"]=>
              array(3) {
                ["b_id"]=>
                string(1) "3"
                ["b_name"]=>
                string(2) "B3"
                ["c_list"]=>
                array(1) {
                  ["4C4"]=>
                  array(3) {
                    ["c_id"]=>
                    string(1) "4"
                    ["c_name"]=>
                    string(2) "C4"
                    ["d_list"]=>
                    &NULL
                  }
                }
              }
            }
          }
        }
        

        【讨论】:

        • 我发现您的解决方案有点笨拙,必须单独处理最后一个条目。它对可达到的级别有一个(公认的高)限制。据我所知,我的解决方案是真正返回 op 提出的样本的唯一解决方案,对深度没有任何限制,包括级别数和每个级别上保留的键值对,并允许对这些进行实际参数化键。每个人都有硬编码的键和非递归行为。
        • @FélixGagnon-Grenier 我不认为你的回答很好是拒绝他人投票的原因。
        • 不,这是因为你的不好:它不是递归的,它不是可参数化的,它不能深入。你真的看过我的评论吗? ;) 因此,你真的认为你的 revenge downvoting 更好吗?想评论一下我到底有什么不好的地方吗?
        • noop,你看到我的答案了吗,现在它至少从 a 到 y 走了 25 级。
        • 是的,25 是一个限制。不是无限。我已经非常仔细地阅读了。另外,如果 a_ 只是一个例子,并且可以动态改变呢? OP 是否必须针对每个不同的用例重写​​您的函数?如果突然有更多值怎么办,OP 将不得不再次重写它以包含更多值?我不确定,但我开始认为我们这里有语言障碍。 无限对你来说意味着同样的事情吗?因为25和无限不一样。您了解 OP 的样本和您的结果之间的区别吗?你的结果不是json。因此,您没有回答问题。
        【解决方案5】:

        这很有趣。据我所知,您正在尝试将平面数组转换为多维数组,以及将 keys 转换为多维表示。

        顶级差异似乎存在于a_* 键的下划线之前的部分。

        然后,对于这些键中的每一个,每隔一个*_ 字母应该会产生它自己的列表。

        此递归函数无需硬编码即可解决问题,适用于任意数量的级别、字母(或其他任何内容)和正确的标识符。

        它似乎完全返回了您在示例中显示的 json($array 是您问题中定义的数组)

        $multidimension = multidimensionalify($array, ['a', 'b', 'c'], ['name']);
        var_dump(json_encode($multidimension, JSON_PRETTY_PRINT));
        
        function multidimensionalify(
            array $input,
            array $topLevelLetters,
            array $rightHandIdentifiers,
            $level = 0,
            $parentId = null,
            $uniqueString = 'id'
        )
        {
            $thisDimension = [];
            $thisLetter = $topLevelLetters[$level];
            foreach ($input as $entry)
            {
                $thisId = $entry["{$thisLetter}_{$uniqueString}"];
                $condition = true;
                if ($parentId !== null)
                {
                    $parentLetter = $topLevelLetters[$level - 1];
                    $condition = $entry["{$parentLetter}_{$uniqueString}"] === $parentId;
                }
                if (!isset($thisDimension[$thisId]) && $condition)
                {
                    $thisObject = new stdClass;
                    $thisObject->{"{$thisLetter}_{$uniqueString}"} = $thisId;
                    foreach ($rightHandIdentifiers as $identifier)
                    {
                        $thisObject->{"{$thisLetter}_{$identifier}"} = $entry["{$thisLetter}_{$identifier}"];
                    }
                    if (isset($topLevelLetters[$level + 1])) {
                        $nextLetter = $topLevelLetters[$level + 1];
                        $thisObject->{"{$nextLetter}_list"} = multidimensionalify($input, $topLevelLetters, $rightHandIdentifiers, $level + 1, $thisId, $uniqueString);
                    }
                    $thisDimension[$thisId] = $thisObject;
                }
            }
            return array_values($thisDimension);
        }
        

        【讨论】:

          【解决方案6】:

          试试这个函数,只需传递你的数组和键名进行分组,然后转换为 json。

          public function _group_by($array, $key) {
              $return = array();
              foreach ($array as $val) {
                  $return[$val[$key]][] = $val;
              }
              return $return;
          }
          

          【讨论】:

          猜你喜欢
          • 2016-05-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-07-10
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多