PHP: How do I compare values in nested associative arrays? - php

I'm trying to wrap my head around how to accomplish this…
I have an that's something like:
$contributors = array(
[0] => array(
[name] => 'John',
[role] => 'author',
),
[1] => array(
[name] => 'Gail',
[role] => 'author',
),
[2] => array(
[name] => 'Beth',
[role] => 'illustrator',
),
)
I'm trying to use this information to construct a detailed byline, like:
Written by John and Gail. Designed by Beth.
I need to compare each role to the previous and next ones in order to:
Use a label before the first instance of each role
Separate multiple instances of the same role with a comma
Use "and" instead of a comma before the last instance of each role
I'm no PHP expert so I'm having a hard time figuring out how to approach this! I have a function to output the right label depending on the role, but it's the comparisons I can't seem to figure out. I've considered foreach and while loops, but neither seems up to the job. I've never used a for loop so I don't know if it applies.
Some additional background:
I'm working with WordPress and the Advanced Custom Fields plugin.
$contributors is the value of an ACF Repeater field, where name and role are subfields. (I've simplified the names to make things easier.)

You can group the array per role and use array_pop to remove the element. implode the remaining array elements and just append the popped value.
$contributors = array(
array(
"name" => 'John',
"role" => 'author',
),
array(
"name" => 'Gail',
"role" => 'author',
),
array(
"name" => 'Jose',
"role" => 'author',
),
array(
"name" => 'Thomas',
"role" => 'author',
),
array(
"name" => 'Beth',
"role" => 'illustrator',
),
array(
"name" => 'Mary',
"role" => 'producer',
),
array(
"name" => 'Criss',
"role" => 'producer',
),
);
//Grouped the names according to role
$grouped = array_reduce($contributors, function($c, $v) {
if ( !isset( $c[ $v['role'] ] ) ) $c[ $v['role'] ] = array();
$c[ $v['role'] ][] = $v['name'];
return $c;
}, array());
//Construct final Array
$final = array();
foreach( $grouped as $key => $group ) {
$last = array_pop( $group );
if ( count( $group ) == 0 ) $final[ $key ] = $last; /* One one name on the role, no need to do anything*/
else $final[ $key ] = implode( ", ", $group ) . " and " . $last;
}
echo "<pre>";
print_r( $final );
echo "</pre>";
This will result to:
Array
(
[author] => John, Gail, Jose and Thomas
[illustrator] => Beth
[producer] => Mary and Criss
)
You can now use it as
echo 'Written by ' . $final["author"] . '. Designed by ' . $final["illustrator"] . '. Produced by ' . $final["producer"];
And will result to:
Written by John, Gail, Jose and Thomas. Designed by Beth. Produced by
Mary and Criss

You can try like this -
$contributors = array(
array(
'name' => 'John',
'role' => 'author',
),
array(
'name' => 'Gail',
'role' => 'author',
),
array(
'name' => 'Ali',
'role' => 'author',
),
array(
'name' => 'Beth',
'role' => 'illustrator',
)
);
#This function prepare array to expected String
function PrepareArray($data){
$combined = '';
if (count($data)>1) {
$last = array_slice($data, -1);
$first = join(', ', array_slice($data, 0, -1));
$both = array_filter(array_merge(array($first), $last), 'strlen');
$combined = join(' and ', $both);
}else{
$combined = implode('', $data);
}
return $combined;
}
$authors = array();
$designers = array();
foreach ($contributors as $key => $value) {
if ($value['role']=='author') {
$authors[] = $value['name']; #Keep Authors name in Array
}else if ($value['role']=='illustrator') {
$designers[] = $value['name']; #Keep Designers name in Array
}
}
$authors = PrepareArray($authors);
$designers = PrepareArray($designers);
echo "Written by {$authors}. Designed by {$designers}.";
Output :
Written by John, Gail and Ali. Designed by Beth.

Try below code:
<?php
$contributors = array(
0 => array(
'name' => 'John',
'role' => 'author',
),
1 => array(
'name' => 'Gail',
'role' => 'author',
),
2 => array(
'name' => 'Beth',
'role' => 'illustrator',
),
3 => array(
'name' => 'Albert',
'role' => 'author',
),
);
$labels = ['author'=>'Written by', 'illustrator'=>'Designed by', 'producer'=>'Produced by'];
$new_contributors = [];
foreach($contributors as $cont){
$new_contributors[$cont['role']][] = $cont['name'];
}
$str = '';
foreach($labels as $role=>$label){
if(isset($new_contributors[$role]) && is_array($new_contributors[$role])){
$str .= ' '.$label.' ';
foreach($new_contributors[$role] as $key=>$new_contributor){
$count = count($new_contributors[$role]);
if(($count - $key) == 1 ){
$str .= ' and ';
$str .= $new_contributor;
}else if(($count - $key) == 2){
$str .= $new_contributor;
}else{
$str .= $new_contributor.', ';
}
}
}
}
echo $str;

Related

Regroup multidimensional array to reverse presentation of many-to-many relationship

I need to perform iterated explosions on values in one column of my two dimensional array, then re-group the data to flip the relational presentation from "tag name -> video id" to "video id -> tag name".
Here is my input array:
$allTags = [
[
"name" => "TAG-ONE",
"video" => "64070,64076,64110,64111",
],
[
"name" => "TAG-TWO",
"video" => "64070,64076,64110,64111",
],
[
"name" => "TAG-THREE",
"video" => "64111",
]
];
I want to isolate unique video ids and consolidate all tag names (as comma-separayed values) that relate to each video id.
Expected output:
$allTagsResult = [
[
"name" => "TAG-ONE,TAG-TWO",
"video" => "64070",
],
[
"name" => "TAG-ONE,TAG-TWO",
"video" => "64076",
],
[
"name" => "TAG-ONE,TAG-TWO",
"video" => "64110",
],
[
"name" => "TAG-ONE,TAG-TWO,TAG-THREE",
"video" => "64111",
],
];
Somehow I did it by checking the value using nested loops but I wish to know if you guys can suggest any shortest method to get the expected output.
If you want to completely remove foreach() loops, then using array_map(), array_walk_recursive(), array_fill_keys() etc. can do the job. Although I think that a more straightforward answer using foreach() would probably be faster, but anyway...
$out1 = array_map(function ($data) {
return array_fill_keys(explode(",", $data['video']), $data['name']); },
$allTags);
$out2 = [];
array_walk_recursive( $out1, function ( $data, $key ) use (&$out2) {
if ( isset($out2[$key])) {
$out2[$key]['name'] .= ",".$data;
}
else {
$out2[$key] = [ 'name' => $data, 'video' => $key ];
}
} );
print_r($out2);
will give...
Array
(
[64070] => Array
(
[name] => TAG-ONE,TAG-TWO
[video] => 64070
)
[64076] => Array
(
[name] => TAG-ONE,TAG-TWO
[video] => 64076
)
[64110] => Array
(
[name] => TAG-ONE,TAG-TWO
[video] => 64110
)
[64111] => Array
(
[name] => TAG-ONE,TAG-TWO,TAG-THREE
[video] => 64111
)
)
if you want to remove the keys, then
print_r(array_values($out2));
The code could be compressed by piling all of the code onto single lines, but readability is more useful sometimes.
Another method if you don't like looping:
$video_ids = array_flip(array_unique(explode(",",implode(",",array_column($allTags,'video')))));
$result = array_map(function($id){
return ['name' => '','video' => $id];
},array_flip($video_ids));
array_walk($allTags,function($tag_data) use (&$result,&$video_ids){
$ids = explode(",",$tag_data['video']);
foreach($ids as $id) $result[$video_ids[$id]]['name'] = empty($result[$video_ids[$id]]['name']) ? $tag_data['name'] : $result[$video_ids[$id]]['name'] . "," . $tag_data['name'];
});
Demo: https://3v4l.org/vlIks
Below is one way of doing it.
$allTags = [
'0' => [
"name" => "TAG-ONE",
"video" => "64070,64076,64110,64111",
],
'1' => [
"name" => "TAG-TWO",
"video" => "64070,64076,64110,64111",
],
'2' => [
"name" => "TAG-THREE",
"video" => "64111",
]
];
$allTagsResult = array();
$format = array();
foreach( $allTags as $a ) {
$name = $a['name'];
$videos = explode(',', $a['video']);
foreach( $videos as $v ) {
if( !isset( $format[$v]) ) {
$format[$v] = array();
}
$format[$v][] = $name;
}
}
foreach( $format as $video => $names) {
$allTagsResult[] = array('name' => implode(',', $names), 'video' => $video);
}
echo '<pre>';
print_r($allTagsResult);
die;
You can check Demo
I am typically in favor of functional style coding, but for this task I feel it only serves to make the script harder to read and maintain.
Use nested loops and explode the video strings, then group by those video ids and concatenate name strings within each group. When finished iterating, re-index the array.
Code: (Demo)
$result = [];
foreach ($allTags as $tags) {
foreach (explode(',', $tags['video']) as $id) {
if (!isset($result[$id])) {
$result[$id] = ['video' => $id, 'name' => $tags['name']];
} else {
$result[$id]['name'] .= ",{$tags['name']}";
}
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'video' => '64070',
'name' => 'TAG-ONE,TAG-TWO',
),
1 =>
array (
'video' => '64076',
'name' => 'TAG-ONE,TAG-TWO',
),
2 =>
array (
'video' => '64110',
'name' => 'TAG-ONE,TAG-TWO',
),
3 =>
array (
'video' => '64111',
'name' => 'TAG-ONE,TAG-TWO,TAG-THREE',
),
)

How can I update the key start from one on the php?

My first array like this :
$photoList = array(
array(
'id' => 1,
'name' => 'chelsea.jpg'
),
array(
'id' => 2,
'name' => 'mu.jpg'
),
array(
'id' => 3,
'name' => 'city.jpg'
)
);
My second array like this :
photo = array('cover1'=>'liverpool.jpg', 'cover2'=>'city.jpg');
My code like this :
$photo = array_filter($photo, function($item) use ($photoList) {
return in_array($item, array_column($photoList, 'name')) ;
});
if (empty($photo))
$photo=null;
If I run : echo '<pre>';print_r($photo);echo '</pre>';, the result like this :
Array
(
[cover2] => city.jpg
)
I want the result like this :
Array
(
[cover1] => city.jpg
)
So if there is no cover1, the cover2 changed to be cover1
How can I do it?
You could re-index the keys like this :
$photoList = array(
array(
'id' => 1,
'name' => 'chelsea.jpg'
),
array(
'id' => 2,
'name' => 'mu.jpg'
),
array(
'id' => 3,
'name' => 'city.jpg'
)
);
$photo = array('cover1'=>'liverpool.jpg', 'cover2'=>'city.jpg');
$photo = array_filter($photo, function($item) use ($photoList) {
return in_array($item, array_column($photoList, 'name')) ;
});
if (empty($photo)){
$photo = null;
} else {
$idx = 1 ;
foreach ($photo as $key => $value) {
unset($photo[$key]);
$photo['cover'.$idx++] = $value;
}
}
print_r($photo);
Outputs :
Array
(
[cover1] => city.jpg
)
I'm not sure about what you really need. Assuming you want to do that recursively, I hope this helps:
$label = 'cover';
$index = 1;
$a_keys = array_keys($photo);
$new_photo = [];
foreach($a_keys AS $k=>$v){
$new_photo[$label.$index] = $photo[$k];
$index++;
}
$photo = $new_photo;
It will rewrite your array with wanted naming style.

PHP how to get value from multidimensional array?

I have an array in a class and want to obtain the 'Apple' key value ('iPhone').
I assume a for each loop needs to be used.
How would I go about doing this?
UPDATE: I also need to specify the customerType and productType key values.
class Products {
public function products_list() {
$customerType = 'national';
$productType = 'phones';
$phoneType = 'Apple';
$productsArr = array();
$productsArr[] = array(
'customerType' => 'national',
'productType' => array(
'phones' => array(
'Apple' => 'iPhone',
'Sony' => 'Xperia',
'Samsung' => 'Galaxy'
),
'cases' => array(
'leather' => 'black',
'plastic' => 'red',
),
),
'international' => array(
'phones' => array(
'BlackBerry' => 'One',
'Google' => 'Pixel',
'Samsung' => 'Note'
),
'cases' => array(
'leather' => 'blue',
'plastic' => 'green'
),
)
);
}
}
I have created a function that you can more or less give it any product and it will return the key and value from the array
<?php
class Products
{
public function products_list()
{
$customerType = 'national';
$productType = 'phones';
$phoneType = 'Apple';
$productsArr[] = array(
'customerType' => 'national', 'productType' => array(
'phones' => array(
'Apple' => 'iPhone',
'Sony' => 'Xperia',
'Samsung' => 'Galaxy'
),
'cases' => array(
'leather' => 'black',
'plastic' => 'red',
),
),
'international' => array(
'phones' => array(
'BlackBerry' => 'One',
'Google' => 'Pixel',
'Samsung' => 'Note'
),
'cases' => array(
'leather' => 'blue',
'plastic' => 'green'
),
)
);
echo $this->get_value($phoneType, $productsArr) .'<br>';
echo $this->get_value($customerType, $productsArr) .'<br>';
echo $this->get_value($productType, $productsArr) .'<br>';
}
function get_value($product, array $products)
{
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($products), RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iterator as $key => $value) {
if (is_string($value) && ($key == $product)) {
return 'key ->' . $key .' value ->'.$value;
}
}
return "";
}
}
$phone_products = new Products();
$phone_products->products_list();
To use it within the class just call
$this->get_value($phoneType, $productsArr);
from without the class call
$phone_products = new Products();
echo ($phone_products->get_value($phoneType, $productsArr));
//output: key ->Apple value ->iPhone
NB: $phoneType, $productsArr will either be defined the methods they are being used in or passed from other methods or define global variables within the class.
If you want single entry:
echo $productsArr[0]['productType']['phones']['Apple']."<br />";
If there are multiple data, you can use foreach:
foreach ($productsArr as $prod){
echo $prod['productType']['phones']['Apple']."<br />";
}
just try
foreach($productsArr[0]['productType']['phones'] as $phones){
echo $phones[$phoneType]; }
foreach($productsArr as $key1 => $data1 ){
if(is_array($data1))
foreach($data1 as $key2 => $data2 ){
if(is_array($data2))
foreach($data2 as $key3 => $data3 ){
if(array_key_exists('Apple', $data3)) {
echo $data3['Apple'] ."\n";
}
}
}
}

Know the element level in multidimensional array

Well, I am here again dealing with arrays in php. I need your hand to guide me in the right direction. Suppose the following array:
-fruits
--green
---limon
---mango
--red
---apple
-cars
--ferrari
---enzo
----blue
----black
---318
--lamborg
---spider
---gallardo
----gallado-96
-----blue
-----red
-----gallado-98
The - (hyphen) symbol only illustrates the deep level.
Well, I need to build another array (or whatever), because it should be printed as an HTML select as below:
-fruits
--green
---limon
---mango
--red
---apple
-cars
--ferrari
---enzo
----blue
----black
---318
--lamborg
---spider
---gallardo
----gallado-96
-----blue
-----red
-----gallado-98
Looks that for each level element, it should add a space, or hyphen to determinate that it belongs to a particular parent.
EDIT
The have provide an answer provideng my final code. The html select element will display each level as string (repeating the "-" at the begging of the text instead multi-level elements.
Here's a simple recursive function to build a select dropdown given an array. Unfortunately I'm not able to test it, but let me know if it works. Usage would be as follows:
function generateDropdown($array, $level = 1)
{
if ($level == 1)
{
$menu = '<select>';
}
foreach ($array as $a)
{
if (is_array($a))
{
$menu .= generateDropdown($a, $level+1);
}
else
{
$menu .= '<option>'.str_pad('',$level,'-').$a.'</option>'."\n";
}
}
if ($level == 1)
{
$menu = '</select>';
}
return $menu;
}
OK, I got it with the help of #jmgardhn2.
The data
This is my array:
$temp = array(
array(
'name' => 'fruits',
'sons' => array(
array(
'name' => 'green',
'sons' => array(
array(
'name' => 'mango'
),
array(
'name' => 'banana',
)
)
)
)
),
array(
'name' => 'cars',
'sons' => array(
array(
'name' => 'italy',
'sons' => array(
array(
'name' => 'ferrari',
'sons' => array(
array(
'name' => 'red'
),
array(
'name' => 'black'
),
)
),
array(
'name' => 'fiat',
)
)
),
array(
'name' => 'germany',
'sons' => array(
array(
'name' => 'bmw',
)
)
),
)
)
);
Recursive function
Now, the following function will provide an array with items like [level] => [name]:
function createSelect($tree, $items, $level)
{
foreach ($tree as $key)
{
if (is_array($key))
{
$items = createSelect($key, $items, $level + 1);
}
else
{
$items[] = array('level' => $level, 'text' => $key);
}
}
return $items;
}
Calling the funcion
Now, call the function as below:
$items = createSelect($temp, array(), 0);
Output
If you iterate the final $items array it will look like:
1fruits
2green
3mango
3banana
1cars
2italy
3ferrari
4red
4black
3fiat
2germany
3bmw

Simple question on foreach loop question with array with 2 variables. (with code)

How can I edit this foreach loop so that I will be able to use strpos to look if q is found in the label ?
The result array will contain those values.
$q may be anna or ann or reas john
<?php
$q = $_GET["q"];
if (!$q) return;
$data = Array(
Array(
'label' => 'anna c13',
'category' => 'Products'
),
Array(
'label' => 'anders andersson',
'category' => 'People'
),
Array(
'label' => 'andreas johnson',
'category' => 'People'
)
);
$result = array();
foreach ($data as $value) {
array_push($result, array(
"label" => $value["label"],
"category" => $value["category"]
));
}
$json = json_encode($result);
echo $json;
?>
This will output every array in $data where $q is somewhere in 'label'.
<?php
if( !isset( $_GET["q"] )) return;
$q = $_GET["q"];
$data = Array(
Array(
'label' => 'anna c13',
'category' => 'Products'
),
Array(
'label' => 'anders andersson',
'category' => 'People'
),
Array(
'label' => 'andreas johnson',
'category' => 'People'
)
);
$result = array();
foreach ($data as $value) {
if( strpos( $value['label'], $q ) !== false ) {
$result[] = $value;
}
}
$json = json_encode($result);
echo $json;
?>
You haven't defined keys for your $data array - so it automatically take the form of:
array(
0=>array(...),
1=>array(...),
2=>array(...)
)
This means that you're using strtolower on an int - so that's probably why it's failing.
foreach ($data as $value) {
if(strpos($value['label'], $q) !== false){
$result[] = $value;
}
}

Categories