在具有可变数量的分组键的多维数组中求和数据

时间:2017-10-26 17:54:29

标签: php

我有来自MySQL的数据,目标是从多个服务器提取数据。

数据可以按一个或多个字段进行分组,我想从X个MySQL服务器中提取数据,并使用其他数据作为键或索引对应该求和的数据求和。

示例:

来自Mysql的结果[$ x]:

[{"date":"2017-10-10","account":"1","trees":"1","people":"0","pets":"4"}
,{"date":"2017-10-10","account":"2","trees":"2","people":"5","pets":"1"}
,{"date":"2017-10-11","account":"1","trees":"3","people":"3","pets":"4"}
,{"date":"2017-10-11","account":"2","trees":"4","people":"1","pets":"4"}]

来自Mysql的结果[$ x + 1]:

[{"date":"2017-10-10","account":"1","trees":"5","people":"1","pets":"1"}
,{"date":"2017-10-10","account":"2","trees":"5","people":"2","pets":"1"}
,{"date":"2017-10-11","account":"1","trees":"5","people":"0","pets":"2"}
,{"date":"2017-10-11","account":"2","trees":"5","people":"1","pets":"2"}]

期望的最终结果:

[{"date":"2017-10-10","account":"1","trees":"6","people":"1","pets":"5"}
,{"date":"2017-10-10","account":"2","trees":"7","people":"7","pets":"2"}
,{"date":"2017-10-11","account":"1","trees":"8","people":"3","pets":"6"}
,{"date":"2017-10-11","account":"2","trees":"9","people":"2","pets":"6"}]

在这种情况下,有2个键dateaccount,但实际上可以有1到4个键。 分组密钥是已知的,因此代码知道在这种情况下,dateaccount是密钥。

我正在尝试找到一种更好的方法,而不是遍历mysql[$x]的每一行,然后遍历mysql[$x+1] ... mysql[$x+n]的每一行并检查已知的每一行键以查看它们是否与当前行的键值匹配,然后对数据求和。

另请注意,实际上,每组密钥可能没有一行,因此每个服务器的行数和顺序都可以变化。

我需要一种有效的方法来执行此操作,因为可能存在数十万行和十几个+列。

我正在尝试这样的代码:

$res = array();

$keys = array('date','account');
array_walk($t,'rehash',$res);

function rehash($data,$key,&$result) {
    global $keys;
    global $res;
    $keydata = array_slice($data,0,count($keys));
    $vals = array_slice($data,count($keys));

    //this is how the data would ideally be structured, my model
    //$res[$keydata[$keys[0]]][$keydata[$keys[1]]] = $vals;
    //this code below successfully creates rows in the right format
    for($x = count($keys) - 1; $x>= 0; $x--) {
        if($x == count($keys)-1) {
            $tmp = array($keydata[$keys[$x]] => $vals);
        //} else if($x ==0) {
        //  maybe reconcile here?
        } else {
            $tmp = array($keydata[$keys[$x]] => $tmp);
        }
    }
}

但我该怎么调和呢?我不能简单地做$ res [key1] = $ tmp [key1],因为有了多个键,它会删除所有其他行。

对于嵌套数组,如果级别数是可变的,我将如何遍历每个嵌套数组?

最后,如果我这样做,我需要将其转换回前端的原始格式,所以......快乐。 :D(这部分很容易,从效率的角度来看值得考虑)

2 个答案:

答案 0 :(得分:0)

您不需要使用array_walk,因为您的数据不是分层的。只需使用普通的foreach循环。

$res = array();
foreach ($t as $row) {
    $cur = &$res;
    // Drill down into result array using the key fields, creating nested arrays
    // as necessary
    foreach ($keys as $keycol) {
        $key = $row[$keycol];
        if (!isset($cur[$key])) {
            $cur[$key] = array();
        }
        $cur = &$cur[$key];
    }
    // Now go through the data columns in the row, adding them to the totals
    foreach ($row as $key => $val) {
        if (in_array($key, $keys)) {
            // Put the key fields in the result row
            $cur[$key] = $val;
        } else {
            // Accumulate other values in the result row
            if (!isset($cur[$key])) {
                $cur[$key] = 0;
            }
            $cur[$key] += $row[$key];
        }
    }
    unset($cur); // Break the reference link
}

答案 1 :(得分:0)

这是一个可能的解决方案,基于问题中给出的样本数据的结构。

$jsn1 = '[{"date":"2017-10-10","account":"1","trees":"1","people":"0","pets":"4"}
    ,{"date":"2017-10-10","account":"2","trees":"2","people":"5","pets":"1"}
    ,{"date":"2017-10-11","account":"1","trees":"3","people":"3","pets":"4"}
    ,{"date":"2017-10-11","account":"2","trees":"4","people":"1","pets":"4"}]';


    $jsn2 = '[{"date":"2017-10-10","account":"1","trees":"5","people":"1","pets":"1"}
    ,{"date":"2017-10-10","account":"2","trees":"5","people":"2","pets":"1"}
    ,{"date":"2017-10-11","account":"1","trees":"5","people":"0","pets":"2"}
    ,{"date":"2017-10-11","account":"2","trees":"5","people":"1","pets":"2"}]';


    /* Decode the json strings into associative arrays: */
    $arr1 = json_decode($jsn1, true);
    $arr2 = json_decode($jsn2, true);

    /* Put in an array the keys whose values are to be added: */
    /* Do not include date and account keys if they are not to be added */
    $arr_keys = ['trees', 'people', 'pets'];

    /* Loop through both $arr1 and $arr2 while storing new arrays in arr: */
    /* Be adding up the values for the keys that are in $arr_keys */
    $arr = [];
    foreach($arr1 as $k => $v){
    $arr[$k]['date'] = $arr1[$k]['date']; $arr[$k]['account'] = $arr1[$k]['account'];
    foreach($arr_keys as $y){ 
if($arr1[$k][$y] !== null && $arr2[$k][$y] !== null){
    $arr[$k][$y] = $arr1[$k][$y] + $arr2[$k][$y];
    }}}

    /* encode the array to the required json string: */
    $json = json_encode($arr);

    echo $json; 
    /* Output:
    [{"date":"2017-10-10","account":"1","trees":"6","people":"1","pets":"5"}
    ,{"date":"2017-10-10","account":"2","trees":"7","people":"7","pets":"2"}
    ,{"date":"2017-10-11","account":"1","trees":"8","people":"3","pets":"6"}
    ,{"date":"2017-10-11","account":"2","trees":"9","people":"2","pets":"6"}]
    */