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
Related
I need to extract data from elements with keys that start with foo. from the below array:
[
'name' => 'Bar',
'location' => 'Baz',
'foo.2021-02-01' => '50000.00',
'foo.2021-03-01' => '50000.00',
'foo.2021-04-01' => '50000.00',
'foo.2021-05-01' => '',
]
After identifying qualifying keys, I need to create a new indexed array of associative rows using the date substring from the original keys like so:
[
['date' => '2021-02-01', 'value' => '50000.00'],
['date' => '2021-03-01', 'value' => '50000.00'],
['date' => '2021-04-01', 'value' => '50000.00'],
['date' => '2021-05-01', 'value' => ''],
]
I've been able to extract the keys like so:
$keys = array_keys($theData[0]);
foreach ( $keys as $key ) {
if ( preg_match( '/foo.*/', $key ) ) {
$line = explode('.', $key);
$item[]['name'] = $line[1];
}
}
but I'm losing the values.
I then tried looping through the array manually and rebuilding the desired outcome, but the keys will change so I don't know how future-proof that would be.
Is there a wildcard approach I can take to achieve this?
You almost had it:
<?php
$theData = [
'name' => 'Bar',
'location' => 'Baz',
'foo.2021-02-01' => '50000.00',
'foo.2021-03-01' => '50000.00',
'foo.2021-04-01' => '50000.00',
'foo.2021-05-01' => ''
];
$item = [];
// No need for array_keys(), foreach() can already do this
foreach( $theData as $key => $value )
{
// check if the key starts with foo.
// Regular expressions are heavy; if you'd like then substitute with:
// if ( substr( $key, 0, 4 ) === 'foo.' )
if ( preg_match( '/^foo\\./', $key ) )
{
// foo. is 4 chars long so substring from the fourth index till the end
$item[] = [
'date' => substr( $key, 4 ),
'value' => $value
];
}
}
var_dump( $item );
Output:
array(4) {
[0]=>
array(2) {
["date"]=>
string(10) "2021-02-01"
["value"]=>
string(8) "50000.00"
}
[1]=>
array(2) {
["date"]=>
string(10) "2021-03-01"
["value"]=>
string(8) "50000.00"
}
[2]=>
array(2) {
["date"]=>
string(10) "2021-04-01"
["value"]=>
string(8) "50000.00"
}
[3]=>
array(2) {
["date"]=>
string(10) "2021-05-01"
["value"]=>
string(0) ""
}
}
A simple loop, checking for the key starting with foo. and then a little code to replace foo. in the key with nothing will do the trick
If you have PHP8 or >
$arr = [
'name' => 'Bar',
'location' => 'Baz',
'foo.2021-02-01' => '50000.00',
'foo.2021-03-01' => '50000.00',
'foo.2021-04-01' => '50000.00',
'foo.2021-05-01' => ''
];
$new = [];
foreach ($arr as $k => $v){
if ( str_starts_with( $k , 'foo.' ) ) {
$new[] = ['date' => str_replace('foo.', '', $k), 'value' => $v];
}
}
print_r($new);
RESULT
Array
(
[0] => Array
([date] => 2021-02-01, [value] => 50000.00)
[1] => Array
([date] => 2021-03-01, [value] => 50000.00)
[2] => Array
([date] => 2021-04-01, [value] => 50000.00)
[3] => Array
([date] => 2021-05-01, [value] => )
)
Alternatively, for PHP versions prior to PHP8
$new = [];
foreach ($arr as $k => $v){
if ( strpos( $k , 'foo.') !== FALSE && strpos( $k , 'foo.') == 0 ) {
$new[] = ['date' => str_replace('foo.', '', $k), 'value' => $v];
}
}
Using str_starts_with and explode
$arr = [];
foreach ($theData as $k => $v){
if (str_starts_with($k, "foo."))
$arr[] = ["date" => explode(".", $k)[1], "value" => $v];
}
var_dump($arr);
sscanf() is an ideal function to call which will both check for qualifying strings and extract the desired trailing date value. It doesn't use regex, but it does require a placeholder %s to target the date substring. If a given string doesn't qualify, no element is pushed into the result array.
Code: (Demo) (without compact())
$result = [];
foreach ($array as $key => $value) {
if (sscanf($key, 'foo.%s', $date)) {
// $result[] = ['date' => $date, 'value' => $value];
$result[] = compact(['date', 'value']);
}
}
var_export($result);
If you remove the optional technique of using compact(), this solution makes fewer function calls than all other answers on this page.
I would probably only use regex if I wanted to strengthen the validation for qualifying key strings. (Demo)
$result = [];
foreach ($array as $key => $value) {
if (preg_match('~^foo\.\K\d{4}-\d{2}-\d{2}$~', $key, $m)) {
$result[] = ['date' => $m[0], 'value' => $value];
}
}
var_export($result);
How I can search array key from multidimensional array and return found key value with parent array key if it's exist. For example I've custom array:
$array = [
'type' => 'vacancy',
'needs' => ['root' => 'active'],
'market' => 'shopping',
'red' => 'color',
'education' => 'learning',
'fruits' => [
'red' => 'apple',
'cool' => 'cherry'
]
];
For example I need search key red from current array. As you see in this array exist 2 items with key red. If array have similar keys then function return example array as response:
[
0 => ['red' => 'color'],
1 => ['red' => 'apple']
]
If search key (example type) is only once inside array then response will be like this:
['type' => 'vacancy']
I tired:
function searchKey($key, $array) {
foreach ($array as $k => $v) {
if($key === $k) {
return [$k => $v];
} elseif(is_array($v)) {
return searchKey($key, $v);
} elseif(is_array($k)) {
return searchKey($key, $k);
}
}
return false;
}
When I search key root from array result is correct but when I searching key red return false. How can be solved my function or has any performance methods for searching key and get result for my needs?
You can try this :
function findByKey($findKey, $array, $result = []) {
foreach ($array as $key => $value) {
if ($key === $findKey) {
$result[] = [$key => $value];
}
if (is_array($value)) {
$result = findByKey($findKey, $value, $result);
}
}
return $result;
}
The idea is to use a recursive function :
you loop through your array
for each key => value, you check if the key is what you want : if yes, add it to the result array, else go next
if the value is an other array, you search inside this array if you have the key you want
Now use it :
$array = [
'type' => 'vacancy',
'needs' => ['root' => 'active'],
'market' => 'shopping',
'red' => 'color',
'education' => 'learning',
'fruits' => [
'red' => 'apple',
'cool' => 'cherry'
]
];
With key type :
$result = findByKey('type', $array);
var_dump($result);
Output is :
array(1) {
[0]=>
array(1) {
["type"]=>
string(7) "vacancy"
}
}
With key red :
$result = findByKey('red', $array);
var_dump($result);
Output is :
array(2) {
[0]=>
array(1) {
["red"]=>
string(5) "color"
}
[1]=>
array(1) {
["red"]=>
string(5) "apple"
}
}
Here is a link to test it : link
I have an array like this:
array(
[cat] => news,
[comments_count] => 2,
[meta] => array(
[first_meta] => 44,
[second_meta] => 54,
)
)
The above code is an example of array that I have. Now I wanna make the above array clear like this:
array(
[cat] => news,
[comments_count] => 2,
[first_meta] => 44,
[second_meta] => 54,
)
(means Delete -meta- but not it's indexes. I want to add indexes of meta to the first array)
Add the meta array to the array and then unset the meta array:
$array = $array + $array['meta'];
unset($array['meta']);
You may use the below function if you have a multidimentional array and you can reuse it anywhere.
function array_flatten($array) {
if (!is_array($array)) {
return false;
}
$result = array();
foreach ($array as $key => $value) {
if (is_array($value)) {
$result = array_merge($result, array_flatten($value));
} else {
$result[$key] = $value;
}
}
return $result;
}
$array = array(
'cat' => 'news',
'comments_count' => '2',
'meta' => array(
'first_meta' => '44',
'second_meta' => '54',
)
);
var_dump(array_flatten($array));
The result will be
array(4) {
["cat"]=>
string(4) "news"
["comments_count"]=>
string(1) "2"
["first_meta"]=>
string(2) "44"
["second_meta"]=>
string(2) "54"
}
Otherwise if you just need to flatten meta array as in your question. array_merge() the meta array and unset meta it as below.
$result = array_merge($array, $array["meta"]);
unset($result["meta"]);
var_dump($result);
I have it like this:
$data = array(
"City_0" => "London",
"City_1" => "Paris",
"City_2" => "Lisbon",
"City_3" => "Berlin"
);
plus some other data in that same array.
User will select only one of these and what I need is:
Check with preg_match to get all keys that starts with "city_"
find key which has value (it is not empty), take that value
assign it to new key
remove all "city_" keys
add new key to array with the name "chosen_city" which will contain that value
What I tried:
foreach ($data as $key => $value) {
$matches = preg_match('/city_/i', $key);
if ($value != "") {
$newValue = $value;
break;
}
}
$data['chosen_city'] = $newValue;
print_r($data);
This works partially, how can I remove all previous "city_" keys from array in that if statement?
NOTE:
I have other keys in array, and I don't want to remove them as well.
Input array:
$data = array(
"City_0" => "London",
"City_1" => "Paris",
"City_2" => "Lisbon",
"City_3" => "Berlin",
"distance" => "5 km",
"days" => "7",
"tickets" => "2",
"discount" => "10%",
);
Expected output:
$data = array(
"chosen_city" => "Berlin",
"distance" => "5 km",
"days" => "7",
"tickets" => "2",
"discount" => "10%",
);
Thanks.
Please put unset for example code :
$data = array( "City_0" => "London", "City_1" => "Paris", "City_2" => "Lisbon", "City_3" => "Berlin");
foreach($data as $key => $value){
$matches = preg_match('/city_/i', $key);
if($matches && $value != ""){
$newValue = $value;
unset($data[$key]);
}elseif($matches){
unset($data[$key]);
}
}
$data['chosen_city'] = $newValue;
preg_* is somewhat overkill in this instance - you could use strpos and it'd work just as well
However, the question is 'how to I remove the city_* keys', so to do that, just use unset():
foreach($data as $key => $value){
$matches = preg_match('/city_/i', $key);
if($value != ""){
$newValue = $value;
unset($data[$key]); //Remove this item from the array
break;
}
}
$data['chosen_city'] = $newValue;
print_r($data);
$data = array(
"City_0" => "London",
"City_1" => "Paris",
"City_2" => "Lisbon",
"City_3" => "Berlin",
"distance" => "5 km",
"days" => "7",
"tickets" => "2",
"discount" => "10%",
);
$value = 'Berlin';
if (array_search($value, $data)) {
$data['chosen_city'] = $value;
foreach ($data as $key=>$val) {
if (stripos($key, 'city_')===0) {
unset($data[$key]);
}
}
}
result:
array(5) {
'distance' =>
string(4) "5 km"
'days' =>
string(1) "7"
'tickets' =>
string(1) "2"
'discount' =>
string(3) "10%"
'chosen_city' =>
string(6) "Berlin"
}
If you use PHP >=5.6, probably you can use (I didn't tested this code)
$city = array_search($value, $data);
if ($city) {
$data['chosen_city'] = $value;
$data = array_filter($data,function($key,$val){return stripos($key,'city_')===false;},ARRAY_FILTER_USE_BOTH);
}
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;
}