Merge multi-dimensional arrays and sum column values which share a common value in another column

孤人 提交于 2020-01-14 10:22:52

问题


I have 3 arrays for storing posts,comments, and likes.

These are the JSON strings:

//comments JSON (stores user and comment points)

$comments='[
    {
        "user": "5",
        "points": "12"
    },
    {
        "user": "2",
        "points": "1"
    },
    {
        "user": "3",
        "points": "1"
    }
]';

//likes(stores user and likes point)

$likes='[
    {
        "user": "1",
        "points": 7
    },
    {
        "user": "4",
        "points": 4
    },
    {
        "user": "3",
        "points": 1
    }
]';

//posts (stores user and post points)

$posts='[
    {
        "user": "1",
        "points": "6"
    },
    {
        "user": "3",
        "points": "2"
    },
    {
        "user": "2",
        "points": "1"
    }
]';

I convert these JSONs into arrays like this:

$comment_array  =   json_decode($comments,TRUE); 
$like_array     =   json_decode($likes,TRUE); 
$post_array     =   json_decode($posts,TRUE); 

//echo '<pre>';
//print_r($comment_array);
//print_r($like_array);
//print_r($post_array);
//echo '</pre>';

Now, I'm trying to sum these points and save the result in a new array. It's not mandatory that a user should have entries in all the three arrays. It depends on whether a user has made a comment, post or like.

function mergeArrays($filenames, $titles, $descriptions) {
    $result = array();

    foreach ( $filenames as $key=>$name ) {
        $result[] = array( 'filename' => $name, 'title' => $titles[$key], 'descriptions' => $descriptions[ $key ] );
    }

    return $result;
}

The above function can merge all the three arrays.

$merged= mergeArrays($comment_array, $like_array, $post_array);

echo '<pre>';
print_r($merged);
echo '</pre>';

However, each array after merging is stored as an index element.

How can I get a result something like this:

$result='[
    {
        "user": "1",
        "points": "13"
    },
    {
        "user": "2",
        "points": "2"
    },
    {
        "user": "3",
        "points": "4"
    },
    {
        "user": "4",
        "points": "4"
    },
    {
        "user": "5",
        "points": "12"
    }
]';

回答1:


Considering your three arrays, this code will get you an array with: points, votes and diferent users.

Edit: Adding additional array and printing it to get the output desired by question.

$points = 0;

$uniqueUsers = array();
$votes = 0;
$users = 0;

$result = array();

//Comments
if (!empty($comment_array)) {
    foreach ($comment_array as $item) {

        if (!in_array($item['user'], $uniqueUsers)) {
            array_push($uniqueUsers, $item['user']);
            $result[$item['user']] = 0;
        }
        $votes ++;
        $result[$item['user']] += $item['points'];
    }
}

// Likes
if (!empty($like_array)) {
    foreach ($like_array as $item) {

        if (!in_array($item['user'], $uniqueUsers)) {
            array_push($uniqueUsers, $item['user']);
            $result[$item['user']] = 0;
        }
        $votes ++;
        $result[$item['user']] += $item['points'];
    }
}

// Posts
if (!empty($post_array)) {
    foreach ($post_array as $item) {

        if (!in_array($item['user'], $uniqueUsers)) {
            array_push($uniqueUsers, $item['user']);
            $result[$item['user']] = 0;
        }
        $votes ++;
        $result[$item['user']] += $item['points'];

    }
}


foreach ($result as $idUser=>$points) {
    echo "\n";
    echo "\n" . 'User: ' . $idUser;
    echo "\n" . 'Points: ' . $points;
}


$results = array('users'=> count($uniqueUsers), 'votes'=>$votes, 'points'=> $points);

//print_r($results);



回答2:


The solution using array_column, array_walk_recursive and array_values functions:

...
$comments = array_column($comment_array, 'points', 'user');
$likes = array_column($like_array, 'points', 'user');
$posts = array_column($post_array, 'points', 'user');

$list = [$comments, $likes, $posts];
$result = [];

array_walk_recursive($list, function($v, $k) use(&$result){
    if (key_exists($k, $result)){
        $result[$k]['points'] += $v;
    } else {
        $result[$k] = ['user' => $k, 'points' => $v];
    }
});
$result = array_values($result);

print_r($result);

The output:

Array
(
    [0] => Array
        (
            [user] => 5
            [points] => 12
        )

    [1] => Array
        (
            [user] => 2
            [points] => 2
        )

    [2] => Array
        (
            [user] => 3
            [points] => 4
        )

    [3] => Array
        (
            [user] => 1
            [points] => 13
        )

    [4] => Array
        (
            [user] => 4
            [points] => 4
        )
)



回答3:


Do the following to get one array with summed points:

$collections = array(
    'comments' => json_decode($comments,TRUE),
    'likes' => json_decode($likes,TRUE);,
    'posts' => json_decode($posts,TRUE),
);

$newArray = array();

foreach ($collections as $collection) {
    foreach ($collection as $user) {
        $newArray[$user->user] += $user->points;
    }
}



回答4:


There are two important points to make if you want to learn the "best" way to handle these types of operations.

  1. Don't use iterated in_array() calls when isset() can be used instead. This is because isset() is much more efficient than in_array().

  2. Use temporary keys to identify duplicate occurrences, then re-index your results when finished -- usually with array_values(), but this time I used array_multisort() to re-order the results AND re-index.

Code: (Demo)

$merged = array_merge(json_decode($comments, true), json_decode($likes, true), json_decode($posts, true));
$result = [];
foreach ($merged as $entry) {
    if (!isset($result[$entry['user']])) {
        $result[$entry['user']] = $entry;
    } else {
        $result[$entry['user']]['points'] += $entry['points'];
    }
}
array_multisort(array_column($result, 'user'), $result);
// usort($result, function($a, $b) { return $a['user'] <=> $b['user']; });  
// array_multisort() will outperform `usort()` in this case.
echo json_encode($result);

Output:

[{"user":"1","points":13},{"user":"2","points":2},{"user":"3","points":4},{"user":"4","points":4},{"user":"5","points":"12"}]
  • Decode each array and merge them together into a multi-dimensional array.
  • Iterate each subarray and determine if it is the first occurrence of the user. If so, retain the entire subarray. If not, only increase the points tally within that subarray.
  • When the loop is finished, sort by user ascending.

This is clean, direct, and readable.



来源:https://stackoverflow.com/questions/37592427/merge-multi-dimensional-arrays-and-sum-column-values-which-share-a-common-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!