【问题标题】:Convert flat array to multidimensional array with dynamic keys. Is it possible?使用动态键将平面数组转换为多维数组。是否可以?
【发布时间】:2021-12-30 05:12:35
【问题描述】:

首先,我有以下从数据库返回的数据。我将有两个不同的数据分别如下

sum1 count1 sm__state_name__ om__order_date__year om__order_date__quarter om__order_date__month
5645000 4 Luanda 2017 3 8
213985939.8600001 1606 Luanda 2017 3 9
7729331.52 119 Benguela 2017 3 9
1012936 17 Zaire 2017 3 9
1054883 19 Bie 2017 3 9
2347944 26 Cuando Cubango 2017 3 9
428769.6000000001 60 Bengo 2017 3 9
6444569 86 Huila 2017 3 9
4914030 25 Cunane 2017 3 9
1167200 26 Cuanza North 2017 3 9
750080 10 Cuanza Sul 2017 3 9
2178100 6 Huambo 2017 3 9
1099934 25 Lunda North 2017 3 9
410135 12 Malange 2017 3 9

数组格式

   array (
  0 => 
  array (
    'sum1' => '5645000',
    'count1' => '4',
    'sm__state_name__' => 'Luanda',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '8',
  ),
  1 => 
  array (
    'sum1' => '213985939.8600001',
    'count1' => '1606',
    'sm__state_name__' => 'Luanda',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
  2 => 
  array (
    'sum1' => '352839.60000000003',
    'count1' => '9',
    'sm__state_name__' => NULL,
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
  3 => 
  array (
    'sum1' => '7729331.52',
    'count1' => '119',
    'sm__state_name__' => 'Benguela',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
  4 => 
  array (
    'sum1' => '1012936',
    'count1' => '17',
    'sm__state_name__' => 'Zaire',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
  5 => 
  array (
    'sum1' => '1054883',
    'count1' => '19',
    'sm__state_name__' => 'Bie',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
  6 => 
  array (
    'sum1' => '2347944',
    'count1' => '26',
    'sm__state_name__' => 'Cuando Cubango',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
  7 => 
  array (
    'sum1' => '428769.6000000001',
    'count1' => '60',
    'sm__state_name__' => 'Bengo',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
  8 => 
  array (
    'sum1' => '6444569',
    'count1' => '86',
    'sm__state_name__' => 'Huila',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
  9 => 
  array (
    'sum1' => '4914030',
    'count1' => '25',
    'sm__state_name__' => 'Cunane',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
  10 => 
  array (
    'sum1' => '1167200',
    'count1' => '26',
    'sm__state_name__' => 'Cuanza North',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
  11 => 
  array (
    'sum1' => '750080',
    'count1' => '10',
    'sm__state_name__' => 'Cuanza Sul',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
  12 => 
  array (
    'sum1' => '2178100',
    'count1' => '6',
    'sm__state_name__' => 'Huambo',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
  13 => 
  array (
    'sum1' => '1099934',
    'count1' => '25',
    'sm__state_name__' => 'Lunda North',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
  14 => 
  array (
    'sum1' => '410135',
    'count1' => '12',
    'sm__state_name__' => 'Malange',
    'om__order_date__year' => '2017',
    'om__order_date__quarter' => '3',
    'om__order_date__month' => '9',
  ),
)
Array
(
    "sm__state_name__",
    "om__order_date__year",
    "om__order_date__quarter",
    "om__order_date__month",
)

ABOBE 数组包含可以是任意数量的字段

以下是我想要的回报的示例输出

{
    "data": [
        {
            "key": "Luanda",
            "items": [
                {
                    "key": 2017,
                    "items": [
                        {
                            "key": 3,
                            "items": [
                                {
                                    "key": 8,
                                    "items": null,
                                    "count": 4,
                                    "summary": [
                                        438380.9935
                                    ]
                                },
                                {
                                    "key": 9,
                                    "items": null,
                                    "count": 1606,
                                    "summary": [
                                        438380.9935
                                    ]
                                },
                            ],
                            "summary": [
                                1285085.9636
                            ]
                        }
                    ],
                    "summary": [
                        1285085.9636
                    ]
                }
            ],
            "summary": [
                1285085.9636
            ]
        },
        {
            "key": "Benguela",
            "items": [
                {
                    "key": 2017,
                    "items": [
                        {
                            "key": 3,
                            "items": [
                                {
                                    "key": 9,
                                    "items": null,
                                    "count": 679,
                                    "summary": [
                                        4781987.8575
                                    ]
                                },
                            ],
                            "summary": [
                                15017212.0305
                            ]
                        }
                    ],
                    "summary": [
                        15017212.0305
                    ]
                }
            ],
            "summary": [
                15017212.0305
            ]
        },
        {...},
        {...},
        {...},
    ],
    "totalCount": 22854
}

不要介意汇总值。我只是把虚拟值放在那里。

这种过程甚至可能吗?因为我想到了很多不同的东西,递归、多重循环、三重循环,但想不出这种方法可行。

我知道这不是问题或错误。对此感到抱歉。但如果有人能指出正确的方向,那就太好了。

【问题讨论】:

  • 是的,但是可以,如果数据是固定的,您可以使用嵌套循环,也可以将 sql 的数据以数组格式粘贴,这样我们就可以在不将表数据复制到数组的情况下对其进行测试跨度>
  • @Jerson 我已经编辑了表格数据的数组格式的代码。
  • 可以用var_export代替print_r来打印吗
  • 等一下
  • 深嵌套的摘要从何而来?

标签: php arrays


【解决方案1】:

对于需要按层次顺序分组的可变列数组,您肯定需要递归解决这个问题。对于递归调用中的每一步,检查是否存在特定的分组级别,如果不存在,则对其进行初始化。使用关联数组对您的项目进行分组,然后转换为平面数组。从概念上讲,它非常简单,虽然看起来可能有点混乱:

function aggregateData($data, $db_row, $columns, $first_column = true) {
    // Base case: with no more columns left, we just take the sum and return.
    if(empty($columns)) {
        $data['summary'] += $db_row['sum1'];
        return $data;
    }

    $column = array_shift($columns);
    $value = $db_row[$column];
    if($first_column) {
        // First column is a special case. We don't add anything here because every level's summary is the sum of its nested items.
        if(!array_key_exists($value, $data)) {
            $data[$value] = [
                'key'=>$value,
                'items'=>empty($columns) ? null : [],
                'summary'=>0
            ];
        }

        $data[$value] = aggregateData($data[$value], $db_row, $columns, false);
    } else {
        // For all other columns, we add the sum to each nested level.
        if(!array_key_exists($value, $data['items'])) {
            $data['items'][$value] = [
                'key'=>$value,
                'items'=>empty($columns) ? null : [],
                'summary'=>0
            ];
        }

        $data['summary'] += $db_row['sum1'];
        $data['items'][$value] = aggregateData($data['items'][$value], $db_row, $columns, false);
    }

    return $data;
}

function flattenData($data) {
    foreach($data as $key=>$value) {
        if(is_null($value['items'])) {
            break;
        }

        $data[$key]['items'] = flattenData($value['items']);
    }

    return array_values($data);
}

$db_rows = /* your DB retrieval code here */;
$columns = /* columns to group by in hierarchical order */;
$data = [];
foreach($db_rows as $db_row) {
    $data = aggregateData($data, $db_row, $columns);
}

$data = flattenData($data);

为了帮助了解发生了什么,请考虑按状态分组的最顶层。在aggregateData() 调用之后,在展平数组之前,它会生成一个如下所示的结构:

{
    "Luanda": {
        "key": "Luanda",
        "items": {...},
        "summary": ...,
    },
    "Benguela": {
        "key": "Benguela",
        "items": {...},
        "summary": ...,
    }
}

请注意,由于每个条目都与其在对象中的键相关联,而不是与数组中的索引相关联,因此可以轻松查找,以便我们可以汇总每个级别的信息。展平后,我们得到的是:

[
    {
        "key": "Luanda",
        "items": [...],
        "summary": ...,
    },
    {
        "key": "Benguela",
        "items": [...],
        "summary": ...,
    }
]

每个条目不再与其键关联,而是成为所需的平面数组。我们失去了进行简单查找的能力,但在计算结束时我们不再需要这种能力。

以上内容并不能解决您的全部问题,因为此结果中未包含数据点,但由于 stackoverflow 不是免费的编码服务并且您没有提供任何自己的代码,我将留下必要的修改作为练习。但是,这应该会消除所需的大部分工作,并为您的解决方案提供一个强有力的起点。

【讨论】:

  • 我很抱歉,也许我没有正确地提出我的问题。 (state, year, quarter, month) 四个字段不固定。它可以是任意数量的字段。它将以数组的形式给出;
  • @VivekMakwana 这是您问题的重要信息。请在原始问题中提供以避免混淆。
  • @VivekMakwana 我已经修改了我的解决方案以处理动态的列列表。你会注意到,正如我的回答所说,这不是你问题的完整解决方案,但它会让你达到 85% 或更多。我鼓励您通读解决方案以准确掌握它的工作原理。
  • 天哪。有效。你是一个传奇人物。我一直试图了解发生的事情超过 2 小时。无论如何,非常感谢。
  • @VivekMakwana 没问题!如果它提供了您需要的解决方案,请记住接受答案:)
【解决方案2】:

好的,基本上如果我理解正确的话,你有一个包含一堆记录的数据库。然后,您想要创建一些基于数据库的大量 JSON,该数据库将基于状态、年、季度和月的列具有 4 个维度。然后最外层的数组将包含按州分组的数据库记录,然后任何特定州的项目将进一步根据年份的键收缩成一组项目,等等。

我认为最有效的方法是一个循环,您可以在其中提取数据库中的所有记录,然后在 PHP 中循环遍历它,并根据当前记录迭代构造一些新数组,这些数组可以被引用供以后查找。

从设置开始:

$dataItems = [];

这将用于存储和结构化数据,以便轻松查找和计算。

遍历列表。第一项是:

sum1 count1 state_name order_year order_quarter order_month
5645000 4 Luanda 2017 3 8

然后您将运行看起来像这样的迭代代码,以帮助填充您要构建的一个或多个数组:

if (empty($dataItems['Luanda'])) {
    $dataItems['Luanda'] = [];
}
if (empty($dataItems['Luanda'][2017])) {
    $dataItems['Luanda'][2017] = [];
}
if (empty($dataItems['Luanda'][2017][3])) {
    $dataItems['Luanda'][2017][3] = [];
}
if (empty($dataItems['Luanda'][2017][3][8])) {
    $dataItems['Luanda'][2017][3][8] = [];
}
$dataItems['Luanda'][2017][3][8][] = ['sum1' => 5645000, 'count1' => 4];

第二个项目是:

sum1 count1 state_name order_year order_quarter order_month
213985939.8600001 1606 Luanda 2017 3 9

本次迭代的 PHP 代码如下所示:

if (empty($dataItems['Luanda'])) {
    $dataItems['Luanda'] = [];
}
if (empty($dataItems['Luanda'][2017])) {
    $dataItems['Luanda'][2017] = [];
}
if (empty($dataItems['Luanda'][2017][3])) {
    $dataItems['Luanda'][2017][3] = [];
}
if (empty($dataItems['Luanda'][2017][3][9])) {
    $dataItems['Luanda'][2017][3][9] = [];
}
$dataItems['Luanda'][2017][3][9][] = ['sum1' => 213985939.8600001, 'count1' => 1606];

等等。 然后,您将在 $dataItems 结构构建后循环并执行样式化逻辑,例如使用名为“items”的键等,并创建所需的输出结构,最后使用 json_encode 以 JSON 格式输出。

如果您需要全年的计数,在您的初始循环中,您可以写入一个单独的数组以帮助跟踪它,同时将其添加到一起,然后在创建数组时参考它具有所需的输出结构。您可以轻松地将摘要之类的内容添加到循环中并在此过程中对其进行跟踪。

听起来很有趣,但是是的,您只需要一个循环来创建一个查找或多个查找排序的数组,然后再进行一个循环来引用 loopUp 数组,从而以所需的输出格式获取您的数据。

【讨论】:

  • 我很抱歉,也许我没有正确地提出我的问题。 (state, year, quarter, month) 四个字段不固定。它可以是任意数量的字段。它将以数组的形式给出;
  • @VivekMakwana 我相信该方法将基本保持不变。无论我们按哪一列进行分组,最终都会成为我们检查以确保它存在的数组中的键,然后最终写入它。在检查它是否存在时,可能需要在循环中执行此操作,以确保在写入之前我们具有正确的嵌套深度。可能会开始使用您提到的 4 列并使其正常工作,然后通过为深度添加一个循环继续使其动态化。
猜你喜欢
  • 2020-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-30
  • 2012-02-06
  • 1970-01-01
相关资源
最近更新 更多