简体   繁体   中英

Group rows of a multidimensional array and form comma-separated values within each group

I want to combine arrays having same category id and question id.

Sample input:

$array = [
    [
        'category_id' => 1,
        'question_id' => 1,
        'option_id' => 2,
        'title' => 'Do you wear glasses?',
        'answer' => 'no'
    ],
    [
        'category_id' => 1,
        'question_id' => 2,
        'option_id' => 3,
        'title' => 'Your hobbies ?',
        'answer' => 'movies'
    ],
    [
        'category_id' => 1,
        'question_id' => 4,
        'option_id' => 8,
        'title' => 'what is your status?',
        'answer' => 'single'
    ],
    [
        'category_id' => 1,
        'question_id' => 2,
        'option_id' => 1,
        'title' => 'Your hobbies ?',
        'answer' => 'travel'
    ],
];

The 2nd and 4th arrays contain the same question (same category id and same question id), so I would like to have their answer values merged together as a comma-separated string.

Desired result:

Array(
[0] => Array
    (
        [category_id] => 1
        [question_id] => 1
        [option_id] => 2
        [title] => Do you wear glasses?
        [answer] => no
    )

[1] => Array
    (
        [category_id] => 1
        [question_id] => 2
        [option_id] => 3
        [title] => Your hobbies ?
        [answer] => movies,travel
    )

[2] => Array
    (
        [category_id] => 1
        [question_id] => 4
        [option_id] => 8
        [title] => what is your status?
        [answer] => single
    )
) 

It is okay that the option id and title are overwritten while grouping because I am not using the option id and the title values will be identical within each group.

maybe something like this could work, not sure if there's a better way to do it tho:

new_array=array();
foreach($source_array as $e){
     $insert=1;
     foreach($new_array as $n)
         if(array_search($e['title'], $n)==false){
              $n['answer'].=", ".$e['answer'];
              $insert=0;   
         }
     }
     if($insert){
         $new_array[]=$e;
     }
}

For an example array

Array
(
[0] => Array
    (
        [category_id] => 1
        [question_id] => 1
    )

[1] => Array
    (
        [category_id] => 2
        [question_id] => 2
    )

[2] => Array
    (
        [category_id] => 1
        [question_id] => 1
    )

)

You could do something like this as an illustration

// Define the array to work on
$array = array( array( 'category_id' => 1, 'question_id'=> 1), array( 'category_id' => 2, 'question_id' => 2),  array( 'category_id' => 1, 'question_id' => 1));

// Initialize the result array containing only unique question_id/category_id combinations
$unique_array = array();

foreach ($array as $key) {
    if (!count($unique_array)) {
        $unique_array[] = $key;
    } else {
        $unique = 1;
        foreach ($unique_array as $check) {
            if ( $check['category_id'] ==  $key['category_id'] && $check['question_id'] == $key['question_id'] ) {
                $unique = 0;
            }
        }
        if ( $unique ) {
            $unique_array[] = $key;
        }
    }
}

print_r($unique_array);

The output of the last print_r would be

Array
(
[0] => Array
    (
        [category_id] => 1
        [question_id] => 1
    )

[1] => Array
    (
        [category_id] => 2
        [question_id] => 2
    )

)

The reason I ask if order matters, is that if it doesn't matter, you can sort your array first, and then only compare each one to its previous sibling:

$source=array(array("cat_id"=>1,"ques_id"=>1,"opt_id"=>2,"title"=>"cid:1, qid:1, oid:2","answer"=>"whatever"),array("cat_id"=>1,"ques_id"=>2,"opt_id"=>3,"title"=>"cid:1, qid:2, oid:3","answer"=>"whatever"),array("cat_id"=>1,"ques_id"=>4,"opt_id"=>8,"title"=>"cid:1, qid:4, oid:8","answer"=>"whatever"),array("cat_id"=>1,"ques_id"=>2,"opt_id"=>1,"title"=>"cid:1, qid:2, oid:1","answer"=>"whtvr"));
print_r($source); // just to debug

usort($source,function($a,$b){
    if($a["cat_id"]<$b["cat_id"])
        return -1;
    elseif($a["cat_id"]>$b["cat_id"])
        return 1;
    elseif($a["ques_id"]<$b["ques_id"])
        return -1;
    elseif($a["ques_id"]>$b["ques_id"])
        return 1;
    elseif($a["opt_id"]<$b["opt_id"])
        return -1;
    elseif($a["opt_id"]>$b["opt_id"])
        return 1;
    else
        return 0;
});

$source=array_reduce($source,function(&$a,$b){
    if(empty($a))
    {
        $a=array($b);
    }
    else
    {
        $cache=array_pop($a);
        if($cache["cat_id"]==$b["cat_id"] && $cache["ques_id"]==$b["ques_id"])
        {
            $cache["answer"].=", ".$b["answer"];
            $a[]=$cache;
        }
        else
        {
            $a[]=$cache;
            $a[]=$b;
        }
    }
    return $a;
},array());
print_r($source);

Outputs:

// first print_r
Array
(
    [0] => Array
        (
            [cat_id] => 1
            [ques_id] => 1
            [opt_id] => 2
            [title] => cid:1, qid:1, oid:2
            [answer] => whatever
        )

    [1] => Array
        (
            [cat_id] => 1
            [ques_id] => 2
            [opt_id] => 3
            [title] => cid:1, qid:2, oid:3
            [answer] => whatever
        )

    [2] => Array
        (
            [cat_id] => 1
            [ques_id] => 4
            [opt_id] => 8
            [title] => cid:1, qid:4, oid:8
            [answer] => whatever
        )

    [3] => Array
        (
            [cat_id] => 1
            [ques_id] => 2
            [opt_id] => 1
            [title] => cid:1, qid:2, oid:1
            [answer] => whtvr
        )

)
// second print_r
Array
(
    [0] => Array
        (
            [cat_id] => 1
            [ques_id] => 1
            [opt_id] => 2
            [title] => cid:1, qid:1, oid:2
            [answer] => whatever
        )

    [1] => Array
        (
            [cat_id] => 1
            [ques_id] => 2
            [opt_id] => 1
            [title] => cid:1, qid:2, oid:1
            [answer] => whtvr, whatever
        )

    [2] => Array
        (
            [cat_id] => 1
            [ques_id] => 4
            [opt_id] => 8
            [title] => cid:1, qid:4, oid:8
            [answer] => whatever
        )

)

Please be noted that you stated in your comment that option_id is irrelevant, so I didn't replace it; you can replace that too inside the anonymous function inside array_reduce if you need to.

$data = array();

//$new is the array in which you have data originally
foreach($new as $arrayK => $arrayV){
    if(!isset($data[$arrayV['category_id']][$arrayV['question_id']]))
        $data[$arrayV['category_id']][$arrayV['question_id']] = $arrayV;
    else{
        $option = $data[$arrayV['category_id']][$arrayV['question_id']]['option_id'] . ',' . $arrayV['option_id'];
        $data[$arrayV['category_id']][$arrayV['question_id']]['option_id'] = $option;

        $answer = $data[$arrayV['category_id']][$arrayV['question_id']]['answer'] . ',' . $arrayV['answer'];
        $data[$arrayV['category_id']][$arrayV['question_id']]['answer'] = $answer;
    }
}

$data will have the required data in mentioned format
Both answer and option will be comma seperated

foreach($arr as $k=>$a)
{
    $common[$k] = search($arr,'category_id',$a['category_id'],'question_id',$a['question_id']);
    $answers = array_map(function($item) { return $item['answer']; }, $common[$k]);
    $options = array_map(function($item) { return $item['option_id']; }, $common[$k]);
    foreach($common[$k] as $temp)
    {
        $finalAns = $temp;
        $finalAns['answer'] = implode(",",$answers);
        $finalAns['option_id'] = implode(",",$options);
    }
    $final[] = $finalAns;
}

$final = array_map("unserialize", array_unique(array_map("serialize", $final)));
echo "<pre>";
print_r($final);

Put Below function in your common function file or in same file.

function search($array, $key, $value, $key1, $value1)
{
    $results = array();

    if (is_array($array))
    {
        if (isset($array[$key]) && $array[$key] == $value && isset($array[$key1]) && $array[$key1] == $value1)
        {
            $results[] = $array;
        }

        foreach ($array as $subarray)
            $results = array_merge($results, search($subarray, $key, $value,$key1,$value1));
    }

    return $results;
}

Your output would be

Array ( [0] => Array ( [category_id] => 1 [question_id] => 1 [option_id] => 2 [title] => Do you wear glasses? [answer] => no )

[1] => Array
    (
        [category_id] => 1
        [question_id] => 2
        [option_id] => 3,1
        [title] => Your hobbies ?
        [answer] => movies,travel
    )

[2] => Array
    (
        [category_id] => 1
        [question_id] => 4
        [option_id] => 8
        [title] => what is your status?
        [answer] => single
    )

)

There is no reason for convoluted logic or multiple loops.

Each group needs to have a unique identifier. Although the asker states that question_id is enough, you can guarantee uniqueness by combining question_id and category_id as a delimited string. Use this "composite identifier" as the first-level key while pushing data into the result array.

When a composite key is re-encountered ), then do not both to save all data -- only append data to the desired element(s).

When finished iterating, re-index the result array to remove the temporary composite keys (if you wish).

Code: ( Demo )

$result = [];
foreach ($array as $row) {
    $compositeKey = $row['category_id'] . '_' . $row['question_id'];
    if (!isset($result[$compositeKey])) {
        $result[$compositeKey] = $row;
    } else {
        $result[$compositeKey]['answer'] .= ",{$row['answer']}";
    }
}
var_export(
    array_values(
        $result
    )
);

Ps Typically, it is better to form subarrays instead of comma-delimited strings. Subarrays allows better access to individual values and flexibility in presentation. If you ever change your mind about how to present the values, you'd otherwise need to remove or replace the commas and print something else. Save the hassle, leave presentation to your (view) presentation loop and implode the subarray values exactly as you wish.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM