Group array values based on key in php? [duplicate] - php

This question already has answers here:
How to group subarrays by a column value?
(20 answers)
Closed 11 months ago.
I have a array like this:
$str=
Array
(
[No] => 101
[Paper_id] => WE3P-1
[Title] => "a1"
[Author] => ABC
[Aff_list] => "University of South Florida, Tampa, United States"
[Abstracts] => "SLA"
)
Array
(
[No] => 101
[Paper_id] => WE3P-1
[Title] => "a2"
[Author] => DEF
[Aff_list] => "University of South Florida, Tampa, United States"
[Abstracts] => "SLA "
)
Array
(
[No] => 104
[Paper_id] => TU5A-3
[Title] => "a3"
[Author] => GHI
[Aff_list] => "University of Alcala, Alcala de Henares, Spain"
[Abstracts] => "Microwave"
)
I want to group elements in the array based upon 'No' as primary key. The output should look like this:
array(6) {
["No"]=>
string(6) "101"
["Paper_id"]=>
string(6) "WE3P-1"
["Title"]=>
string(80) ""a-1"
["Author"]=>
string(14) "ABC"
["Aff_list"]=>
string(51) ""University of South Florida, Tampa, United States""
["Abstracts"]=>
string(5) ""(SLA)"
"
}
array(6) {
["No"]=>
string(3) "104"
["Paper_id"]=>
string(6) "TU5A-3"
["Title"]=>
string(40) "a2"
["Author"]=>
string(20) "DEF"
["Aff_list"]=>
string(48) ""University of Alcala, Alcala de Henares, Spain""
["Abstracts"]=>
string(9) ""Microwave"
"
}
Note that the Author's value got merged with respect to the primary key 'No'.Can anyone help me out from this, please?
I tried doing this:
foreach($paper_info as $element) {
foreach($element as $v) {
$id = $element['No'];
if (!isset($out[$id])) {
$out[$id] = [
'No' => $element['No'],
'Paper_id' => $element['Paper_id'],
'Title' => $element['Title'],
'Authors' => [],
'Aff_list' => $element['Aff_list'],
'Abstracts' => $element['Abstracts']
];
}
$out[$id]['Authors'][] = ['Authors' => $element['Author']];
}
}

You could use a generic function:
function _group_by($array, $key) {
$return = array();
foreach($array as $val) {
$return[$val[$key]][] = $val;
}
return $return;
}
I added some sample code to test
<?php
$list= [
[ 'No' => 101,
'Paper_id' => 'WE3P-1',
'Title' => "a1",
'Author' => 'ABC',
'Aff_list' => "University of South Florida, Tampa, United States",
'Abstracts' => "SLA"
] ,
[ 'No' => 101,
'Paper_id' => 'WE3P-1',
'Title' => "a2",
'Author' => 'DEF',
'Aff_list' => "University of South Florida, Tampa, United States",
'Abstracts' => "SLA"
] ,
[ 'No' => 104,
'Paper_id' => 'TUSA-3',
'Title' => "a3",
'Author' => 'GH1',
'Aff_list' => "University of Alcala, Alcala de Henares, Spain",
'Abstracts' => "Microwave"
] ];
print_r(_group_by($list, 'No'));

The data format in your question is ambiguous, but assuming the structure for $paper_info is what is below, this should get you the output you're looking for.
$paper_info = array(
array(
'No' => "101",
'Paper_id' => "WE3P-1",
'Title' =>"An Electrically-Small, 3-D Cube Antenna Fabricated with Additive Manufacturing",
'Author' => "Ibrahim Nassar",
...
),
array(
'No' => "101",
...
'Author' => "Thomas Weller",
...
)
);
$out = array();
foreach($paper_info as $paper) {
$id = $paper['No'];
if (!isset($out[$id])) {
$out[$id] = $paper;
$out[$id]['Author'] = array();
}
$out[$id]['Author'][] = $paper['Author'];
}
You should also turn on warnings and display errors in your development environment. I have a feeling it will help you. During development you can either configure your php.ini, or insert this code at the beginning of your php script. Just make sure you remove it before pushing to production.
error_reporting(E_ALL);
ini_set('display_errors', '1');

Thanks to crafter for the awesome function, if someone need to group for multiple keys i edited the crafter function to this:
function _group_by($array, $keys=array()) {
$return = array();
foreach($array as $val){
$final_key = "";
foreach($keys as $theKey){
$final_key .= $val[$theKey] . "_";
}
$return[$final_key][] = $val;
}
return $return;
}

Thanks to crater and Fabio's answer. I updated the code to check if the size of the key is not greater than one (1), underscore will not be appended.
function _group_by($array, $keys=array()) {
$return = array();
$append = (sizeof($keys) > 1 ? "_" : null);
foreach($array as $val){
$final_key = "";
foreach($keys as $theKey){
$final_key .= $val[$theKey] . $append;
}
$return[$final_key][] = $val;
}
return $return;
}

I have wrote an another version of Crafter's answer which is remove given key from actual array. May help someone.
public function _group_by($array, $key) {
$return = array();
foreach($array as $val) {
$return[$val[$key]] = $val;
unset($return[$val[$key]][$key]);
}
return $return;
}

Related

Remove duplicates array from a multidimensional array in PHP

How, from this array, I can filter the duplicate values?
Actually, for the same country and city, the data are the same - Except the population changed.
How can I remove the array that contains the higher population?
$arr = array
(
"100" => array(
array(
"country" => 'France',
"city" => 'Paris',
"population" => '1800000',
),
array(
"country" => 'France',
"city" => 'Paris',
"population" => '2000000',
),
array(
"country" => 'France',
"city" => 'Toulouse',
"population" => '500000',
),
)
"101" => array(
array(
"country" => 'Russia',
"city" => 'Moscow',
"population" => '144000000',
)
)
);
So the desired output should be:
$arr = array
(
"100" => array(
array(
"country" => 'France',
"city" => 'Paris',
"population" => '1800000'
),
array(
"country" => 'France',
"city" => 'Toulouse',
"population" => '500000'
),
)
"101" => array(
array(
"country" => 'Russia',
"city" => 'Moscow',
"population" => '144000000',
)
)
);
This is what I tried:
$temp_array = [];
foreach ($array as &$v) {
if (!isset($temp_array[$v['country']] && $temp_array[$v['city']]))
$temp_array[$v[$key]] =& $v;
}
$array = array_values($temp_array);
return $array;
You can first use array_reduce for filtering the lower population (use the combination of country and city as key). Then explode them and reset the array with that min value:
foreach($arr as $k => &$ar) {
$temp = array_reduce($ar, function ($carry, $item) {
$key = $item["country"] . "###" . $item["city"];
$carry[$key] = (isset($carry[$key]) && $item["population"] > $carry[$key]) ? $carry[$key] : $item["population"];
return $carry;
}, []);
$ar = [];
foreach($temp as $k => $val) {
list($country, $city) = explode("###", $k);
$ar[] = array("country" => $country, "city" => $city, "population" => $val);
}
}
Live example: 3lv4
Edit:
You can use array_filter instead the foreach loop to avoid coping:
$ar = array_filter($ar, function ($item) use ($mins) {
$key = $item["country"] . "###" . $item["city"];
return $mins[$key] == $item["population"];
});
from the looks of it the array is already grouped by country, i'm assuming that each array element only has child arrays from the same country. I'd also say it's more sensible to have an array with keys for each country anyway for easier access and filtering down the line so I would say
$populations = [];
foreach($array as $arr) {
if(!isset($populations[$arr['country']])) $populations[$arr['country']] = [];//create an array entry for the country
$cities = [];
foreach($arr as $city) {
if(!isset($cities[$city]) || $cities[$city]['population'] > $city['population']) $cities[$city] = ['population' => $city['population']];//you could put the value straight in, but this means if you expand later you could add extra fields to each cities info such as district, number of unemployed or whatever you're using this for
}
$populations[$arr['country']] = $cities;
}
this is a slightly different output to that which you have outlined, but i think it will make it simpler to use further on as you can access all data for specific countries, and then for cities therein rather than having to continually loop through and check if the child contains a country you are after.
I hope this makes sense, my fiancee is trying to talk to me about ikea at the same time as I'm answering, so it may not be 100% perfect but will point you in a good direction at least
what I did two nested loops, the first gets the subarray that contains all the content for a specific key (e.g. 100 and 101).
next I iterate through the data, and keep a temporary array with two levels, the first will be the country as key, and the second will be the city as key that tracks the lowest population.
once the above is done, I iterate through the temporary array to get the country, city and population in the correct format and append it to a new array. I then substitute the previous array for this newly acquired result.
<?php
$arr = array
(
"100" => array(
array(
"country" => 'France',
"city" => 'Paris',
"population" => '1800000',
),
array(
"country" => 'France',
"city" => 'Paris',
"population" => '2000000',
),
array(
"country" => 'France',
"city" => 'Toulouse',
"population" => '500000',
),
),
"101" => array(
array(
"country" => 'Russia',
"city" => 'Moscow',
"population" => '144000000',
)
)
);
foreach($arr as $key=>$subarr) {
$tmp = array();
foreach($subarr as $v) {
$country = $v['country'];
$city = $v['city'];
$population = $v['population'];
if(isset($tmp[$country])) {
if(isset($tmp[$country][$city])) {
if($tmp[$country][$city] > $population) {
$tmp[$country][$city] = $population;
}
} else {
$tmp[$country][$city] = $population;
}
} else {
$tmp[$country] = array();
$tmp[$country][$city] = $population;
}
}
$res = array();
foreach($tmp as $country=>$cities) {
foreach($cities as $city=>$population) {
$res[] = array('country'=>$country,'city'=>$city,'population'=>$population);
}
}
$arr[$key] = $res;
}
print_r($arr);
You can make a compound array key with the country and city that way it's easy to keep track of what you have looped.
Since city may not be in the arrays then a if it needed to not get a notice.
foreach($arr as $key => $sub){
foreach($sub as $item){
if(isset($item['city'])){
if(!isset($res[$key][$item['country'] . $item['city']])) $res[$key][$item['country'] . $item['city']] = $item;
if($res[$key][$item['country'] . $item['city']] < $item['population']) $res[$key][$item['country'] . $item['city']] = $item;
}else{
if(!isset($res[$key][$item['country']])) $res[$key][$item['country']] = $item;
if($res[$key][$item['country']] < $item['population']) $res[$key][$item['country']] = $item;
}
}
}
var_dump($res);
Output:
array(2) {
[100]=>
array(2) {
["FranceParis"]=>
array(3) {
["country"]=>
string(6) "France"
["city"]=>
string(5) "Paris"
["population"]=>
string(7) "1800000"
}
["FranceToulouse"]=>
array(3) {
["country"]=>
string(6) "France"
["city"]=>
string(8) "Toulouse"
["population"]=>
string(6) "500000"
}
}
[101]=>
array(1) {
["Russia"]=>
array(2) {
["country"]=>
string(6) "Russia"
["population"]=>
string(9) "144000000"
}
}
}
https://3v4l.org/KNuId
If you need to remove the keys such as "FranceToulouse" then just loop the array again and use array_values

Multiple values from one loop php

If I have this array -
$data_item['actor_id'] = $this->input->post('actor_id');
$data_item['item_number'] = $this->input->post('item_number');
Which gives me this array -
Array (
[actor_id] => Array (
[0] => 162652153
[1] => 162652154
)
[item_number] => Array (
[0] => 3
[1] => 6
)
)
I need to get my data into this format -
$data = array(
array(
'actor_id' => '3342' ,
'item_number' => '57567'
),
array(
'actor_id' => '876' ,
'item_number' => '94'
)
);
I have tried various ways of looping through it but I can't seem to get it. Such as two seperate loops like this -
foreach($data_item['actor_id'] as $key => $value){
$thevalue[] = array('actor_id' => $value
);
}
But it is wrong format. Any tips please?
What about:
$d = [
'actor_id' => [
1,
2,
],
'item_number' => [
3,
4,
],
];
$res = [];
foreach ( $d as $key => $data ) {
foreach ( $data as $index => $value ) {
$res [ $index ] [$key] = $value;
}
}
I'll generate:
array(2) {
[0]=>
array(2) {
["actor_id"]=>
int(1)
["item_number"]=>
int(3)
}
[1]=>
array(2) {
["actor_id"]=>
int(2)
["item_number"]=>
int(4)
}
}
HTH
you can make use of the actor_id keys to flatten your data as desired:
$data = array(); // your new array
foreach($data_item['actor_id'] as $key => $value) {
// make sure we have an array for this key and add actor id
if (!isset($data[$key])) $data[$key] = array();
$data[$key]['actor_id'] = $value;
// now check if we have an item number for this key and add it
if (isset($data_item['item_number'][$key]))
$data[$key]['item_number'] = $data_item['item_number'][$key];
}
print_r($data);
The result should print out just as you wanted it. Sorry for not having tested the code my self, just typed it out of my head missing time and interpreter right now :)

Multidimensional array concatenate values of the same id(key)

How can I concatenate a key value if the id(key) value is the same as other id(key) value
PHP
$locations = Array(
[0] => Array(
"id" => 1,
"latitude" => "51.541561",
"longitude", => "84.215",
"content", => "The quick brown"
)
[1] => Array(
"id" => 1,
"latitude" => "51.541561",
"longitude", => "84.215",
"content", => "fox jumps over the lazy dog"
)
[2] => Array(
"id" => 3,
"latitude" => "12.541561",
"longitude", => "32.215",
"content", => "Another content"
)
)
And I want to make it like this:
$locations = Array(
[0] => Array(
"id" => 1,
"latitude" => "51.541561",
"longitude", => "84.215",
"content", => "The quick brown fox jumps over the lazy dog"
)
[2] => Array(
"id" => 3,
"latitude" => "12.541561",
"longitude", => "32.215",
"content", => "Another content"
)
)
Basically I want to concatenate the value in content(key) if the id(key) is the same with other id(key) value.
Any help would be greatly appreciated.
Try this -
$array = array();
foreach ($yourArray as $val) {
if (!array_key_exists($val['id'], $array)) {
$array[$val['id']] = $val;
} else {
$array[$val['id']]['content'] .= ' '.$val['content'];
}
}
hope this helps you :)
/**
* merge 2 arrays and return a new merged array. if same same key exists it will overwrite , unlike array_merge_recursive
* #param $a
* #param $b
* #return array|mixed
*/
public static function mergeArray($a,$b){
$args=func_get_args();
$res=array_shift($args);
while(!empty($args))
{
$next=array_shift($args);
foreach($next as $k => $v)
{
if(is_integer($k))
isset($res[$k]) ? $res[]=$v : $res[$k]=$v;
elseif(is_array($v) && isset($res[$k]) && is_array($res[$k]))
$res[$k]=self::mergeArray($res[$k],$v);
else
$res[$k]=. $v;
}
}
return $res;
}

Not able to create dynamic array in php

I am new in PHP and in below code $strng value is dynamically created for array.
$strng='"Active Life" => "6","Arts & Entertainment" => "4","Beauty & Spa" => "3","Food & Drink" => "1","Hotels" => "10","Local Services" => "8","Nightlife" => "2","Pets" => "9","Public Services" => "7","Shopping" => "5"';
$bussCatry=array($strng);
foreach($bussCatry as $x=>$x_value)
{
error_log( "Key=" . $x . ", Value=" . $x_value);
}
but when I am trying to create array I am failing I am getting below out put.
only one key value pair
Key=0,
Value="Active Life" =>"6",
"Arts & Entertainment" =>"4",
"Beauty & Spa" =>"3",
"Food & Drink" =>"1",
"Hotels" =>"10",
"Local Services" =>"8",
"Nightlife" =>"2",
"Pets" =>"9",
"Public Services" =>"7",
"Shopping" =>"5"
Check out php's array syntax.
Try this:
$bussCatry = array(
'Active Life' => '6',
'Arts & Entertainment' => '4',
'Beauty & Spa' => '3',
'Food & Drink' => '1',
'Hotels' => '10',
'Local Services' => '8',
'Nightlife' => '2',
'Pets' => '9',
'Public Services' => '7',
'Shopping' => '5',
);
array() function does not parse strings.
array("a" => "orange", "b" => "banana", "c" => "apple")
as you can see in the example there is not just a simple string, there are arguments.
Try this code you have posted wrong code.
$strng=array("Active Life" => "6","Arts & Entertainment" => "4","Beauty & Spa" => "3","Food & Drink" => "1","Hotels" => "10","Local Services" => "8","Nightlife" => "2","Pets" => "9","Public Services" => "7","Shopping" => "5");
//$bussCatry=array($strng);
foreach($strng as $x=>$x_value)
{
echo( "Key=" . $x . ", Value=" . $x_value);
}
Let me know if it works for you :)
Thanks.
Create an array directly assigned values.
$bussCatry = array(
"Active Life" => "6",
"Arts & Entertainment" => "4",
"Beauty & Spa" => "3",
"Food & Drink" => "1",
"Hotels" => "10",
"Local Services" => "8",
"Nightlife" => "2",
"Pets" => "9",
"Public Services" => "7",
"Shopping" => "5"
);
Don't convert the array values into string with single quotes. It doesn't parse like array.
Take a look at token_get_all, you can parse the string into tokens and then build your array as appropriate. Sorry it's a little messy, but it's a decent working starter.
<?php
define('STATE_WAITING_FOR_ARRAY', 1);
define('STATE_WAITING_FOR_KEY', 2);
define('STATE_WAITING_FOR_VALUE', 3);
$string = '"Active Life" => "6","Arts & Entertainment" => "4","Beauty & Spa" => "3","Food & Drink" => "1","Hotels" => "10","Local Services" => "8","Nightlife" => "2","Pets" => "9","Public Services" => "7","Shopping" => "5"';
$string = sprintf('<?php array(%s); ?>', $string);
$tokens = token_get_all($string);
$state = STATE_WAITING_FOR_ARRAY;
$array = array();
foreach($tokens as $token) {
if( ! is_array($token)) {
continue;
}
$name = token_name($token[0]);
$value = $token[1];
if(STATE_WAITING_FOR_ARRAY === $state && 'T_ARRAY' === $name) {
$state = STATE_WAITING_FOR_KEY;
continue;
}
if(STATE_WAITING_FOR_KEY === $state && 'T_CONSTANT_ENCAPSED_STRING' === $name) {
$state = STATE_WAITING_FOR_VALUE;
$key = $value;
continue;
}
if(STATE_WAITING_FOR_VALUE === $state && 'T_CONSTANT_ENCAPSED_STRING' === $name) {
$state = STATE_WAITING_FOR_KEY;
$array[$key] = $value;
$key = null;
continue;
}
}
var_dump($array);
/*
array(10) {
[""Active Life""]=>
string(3) ""6""
[""Arts & Entertainment""]=>
string(3) ""4""
[""Beauty & Spa""]=>
string(3) ""3""
[""Food & Drink""]=>
string(3) ""1""
[""Hotels""]=>
string(4) ""10""
[""Local Services""]=>
string(3) ""8""
[""Nightlife""]=>
string(3) ""2""
[""Pets""]=>
string(3) ""9""
[""Public Services""]=>
string(3) ""7""
[""Shopping""]=>
string(3) ""5""
}
*/
hmm, if this really necessary you can try this:
eval("\$bussCatry = array({$strng});");

zend form Issue in zend framework

I am using zend Framework Form,
I am newbie in zend Framework and i want to display my check box form like this :-
*SK336
*CP
*PES
*JCP
*BGH
*SK996
*KO
*RTY
*HGR
*SK547
*GPK
*SK478
*JUP
Note where :- * is check-box here
what i am trying is here :-
public function init()
{
$parents = array();
$childs = array();
foreach ($this->Tagkey as $aResultDataValue) {
$parents [$aResultDataValue['parent']] = $aResultDataValue['parent'];
$childs [$aResultDataValue['parent']][] = $aResultDataValue['child'];
}
foreach ($parents as $parent){ // print_r ($parents); die();
$tags = new Zend_Form_SubForm();
$tags->addElements(array(
new Zend_Form_Element_MultiCheckbox('parent', array(
'multiOptions' => array($parent),
'filters' => array('StringTrim'),
'validators' => array(
array('InArray',
false,
array($parent))
)
)),
));
foreach ($childs as $child){
$tags->addElements(array(
new Zend_Form_Element_MultiCheckbox('child', array(
'multiOptions' => array($child),
'filters' => array('StringTrim'),
'validators' => array(
array('InArray',
false,
$child)
)
)),
));
}
$this->addSubForms(array(
'tags' => $tags,
)
);
}
I am able to create such type of structure in any .php page but not able to do that right now in zend framework form, I am using zend sub-form here.
Also I got an error right now when i am using this query
Warning: htmlspecialchars() expects parameter 1 to be string, array given in /var/www/dashboard_campaign/library/Zend/View/Abstract.php on line 905
More Information about my Question :-
(1) mysql qyery
select b.tagCode parent,a.tagCode child from tag a, tag b where a.tagParentId=b.tagId
(2) output of Zend_Debug::dump($this->Tagkey);
array(9) {
[0] => array(2) {
["parent"] => string(5) "SK336"
["child"] => string(2) "CP"
}
[1] => array(2) {
["parent"] => string(5) "SK336"
["child"] => string(3) "PES"
}
[2] => array(2) {
["parent"] => string(5) "SK336"
["child"] => string(3) "JCP"
}
[3] => array(2) {
["parent"] => string(5) "SK996"
["child"] => string(2) "KO"
}
[4] => array(2) {
["parent"] => string(5) "SK996"
["child"] => string(3) "RTY"
}
[5] => array(2) {
["parent"] => string(5) "SK996"
["child"] => string(3) "HGR"
}
[6] => array(2) {
["parent"] => string(5) "SK547"
["child"] => string(3) "GPK"
}
[7] => array(2) {
["parent"] => string(5) "SK478"
["child"] => string(3) "JUP"
}
[8] => array(2) {
["parent"] => string(5) "SK336"
["child"] => string(3) "BGH"
}
}
Now i can understand your problem. I think it is to hard for handle that think from sub form. Try to use zend view scripts as following way.
Your form.php
public function init()
{
foreach ($parents as $parent) {
$parent = new Zend_Form_Element_Hidden($parent);
$parent->setDecorators(array(
array(
'ViewScript',
array(
'viewScript' => 'customviewscripts/parent.phtml',
'parent' => $parent
)
)
);
$this->addElement($parent);
}
}
file at views/script/customviewscript/parent.phtml
<?php
$params = $this->element->getDecorator('ViewScript')->getOptions();
$parent = $parems['parent'];
$string = '<label>$parent['name']</label><input type="check" name="parent[]">';
foreach ($children as $child) {
$string .= <label>$child['name']</label>
. <input type="check" name=child[$parent[id]][]>' ;
}
print $string;
?>
This is not the real solution. I percent only example. I think you can customize that. Most of developer use view script to make complex forms.
Your looking like mentioned wrong syntax for multi options John, you should try this.
Remove array for $parent see example below.
new Zend_Form_Element_MultiCheckbox('parent', array(
'multiOptions' => $parent,
'filters' => array('StringTrim'),
'validators' => array(
array('InArray',
false,
array($parent))
)
From db results you have to produce following type of array for the $parent variable
$parent variable should like this for example copy this array and try your self without fetching from database,you will see
all options
$parent=Array ([1] => blah1 [2] => blah2 [3] => blah3 [4] => blah4 [5] => blah5);
Check this one also for multiple check boxes, in place of array you should try placing array variable, I havn't tried this just looked at internet but should work fine.
$category1 = new Zend_Form_Element_MultiCheckbox('categories',Array())
$category1->setLabel('Category 1');
$category2 = new Zend_Form_Element_MultiCheckbox('categories',Array())
$category2->setLabel('Category 2');
... later...
$this->addElement($category1)
->addElement($category2);

Categories