PHP insert index as value in array - php

How can I add to a multidimensional array also the index number of the current added item?
$data_array[] = array('data_array_index' => *the index number of the this array on $data_array*, 'stuff' => 'stuff', etc.)
So when I:
print_r($data_array);
Array(
[0] => Array(
data_array_index => 0
stuff => stuff
)
[25] => Array(
data_array_index => 25
stuff => stuff
)
etc.
Thank you
EDIT
Should this work?
$data_array[] = array('data_array_index' => end($data_array)+1, 'stuff' => 'stuff', etc.)

You could do this:
$array = [
0 => [ "data_array_index" => 0, "stuff" => "stuff" ],
25 => [ "data_array_index" => 25, "stuff" => "stuff" ]
];
$array[] = array('data_array_index' => 0, 'stuff' => 'stuff')
end($array);
$last_id = key($array);
$array[$last_id]['data_array_index'] = $last_id;
I don't know why you want data_array_index in the array because if you put it in a foreach loop you can get the key without needing the variable.
Example:
foreach($key => $data) {
^^^^ The same as `data_array_index`
}

Suppose you have this array:
$data_array = [
0 => [ "data_array_index" => 0, "stuff" => "stuff" ],
25 => [ "data_array_index" => 25, "stuff" => "stuff" ]
];
Now to set a key (note the $data_array[100]):
$data_array[100] = [ "data_array_index" => 100, "stuff" => "stuff" ];

try this once
$arr=array(12=>array("stuff"=>"stuff1"),15=>array("stuff"=>"stuff2"));
foreach($arr as $key=>$val){
$arr[$key]['data_array_index']=$key;
}
echo "<pre>"; print_r($arr);

For my solution see the code below. Beware that this is a very rudimentary function now. It does not provide any fail safe or fallback. If you delete a key it will to fill the space etc.
<?php
// data array
$data_array = [];
// additional info for the array
$info_a = "Lorem Ipsum";
$info_b = "Lorem Ipsum";
// call function
addElement($data_array, $info_a);
addElement($data_array, $info_b);
// display output
echo '<pre>';
print_r($data_array);
echo '</pre>';
function addElement(&$array, $info)
{
// add info to the array
$array[] = [
'stuff'=>$info
];
// get the key of the current array
end($array);
$key = key($array);
// add this information to the array
$array[$key]['data_array_index'] = $key;
}
?>
Output would be
Array
(
[0] => Array
(
[stuff] => Lorem Ipsum
[data_array_index] => 0
)
[1] => Array
(
[stuff] => Lorem Ipsum
[data_array_index] => 1
)
)

Use array_walk
array_walk($data_array,function($value,$key)use($new_array) {
$value['data_array_index'] = $key;
$new_array[$key]=$value;
});
working demo: http://phpfiddle.org/main/code/p937-7cue

Related

PHP Nested Arrays: Imploding all the tree keys of each leaf results in a multidimensional array instead of a 1D associative one

From a nested array, I want to generate the 1D associative array which contains, for each leaf, its ascending keys concatenation.
Summary
Expected results example
1.1. Input
1.2. Output
Actual results example
1.1. Input
1.2. Output
Question
Minimal, Testable Executable Sources
4.1. Explanations
4.2. Sources & Execution
Expected results example
Input
The following nested array:
[
'key1' => 'foo',
'key2' => [
'key3' => [
0 => ['key4' => 'bar' ],
1 => ['key4' => 'azerty']
]
]
]
Output
The following 1D associative array (glue character for the concatenation of the keys: _):
[
'key1' => 'foo',
'key2_key3_0_key4' => 'bar',
'key2_key3_1_key4' => 'azerty'
]
Actual results example
Input
[
'etat' => 'bar',
'proposition_en_cours' => [
'fichiers' => [
0 => ['url_fichier' => 'foo' ],
1 => ['url_fichier' => 'bar']
]
]
]
Output
Array
(
[] => bar
[proposition_en_cours] => Array
(
[fichiers] => Array
(
[0] => Array
(
[url_fichier] => foo
)
[1] => Array
(
[url_fichier] => bar
)
)
)
[proposition_en_cours_fichiers] => Array
(
[0] => Array
(
[url_fichier] => foo
)
[1] => Array
(
[url_fichier] => bar
)
)
[proposition_en_cours_fichiers_0] => foo
[proposition_en_cours_fichiers_0_1] => bar
)
Question
As you can see, the array I get differs in all points from the expected one. I can't figure out why.
Minimal, Testable Executable Sources
Explanations
I initialize an array that must contain all the ascending keys for each leaf: $key_in_db_format = [];.
I iterate on the input array. For each element (leaf or subarray), I pop $key_in_db_format if, and only if, the current depth equals the last depth. If it's an array (i.e.: not a leaf): I add the key to $key_in_db_format. I set a value (the leaf) at the key that is the concatenation of the ascending keys.
Sources & Execution
First, define this array in an empty PHP script of your choice:
$values = [
'etat' => 'bar',
'proposition_en_cours' => [
'fichiers' => [
0 => [ 'url_fichier' => 'foo' ],
1 => [ 'url_fichier' => 'bar' ]
]
]
];
Then, copy/paste the following code and you will be able to execute it:
$values_to_insert_in_meta_table = [];
$iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($values), \RecursiveIteratorIterator::SELF_FIRST);
$last_depth = 0;
$key_in_db_format = [];
foreach ($iterator as $value_key_field => $value_value_field) {
if($iterator->getDepth() == $last_depth) {
array_pop($key_in_db_format);
}
if(is_array($value_value_field)) {
array_push($key_in_db_format, $value_key_field);
} else {
$values_to_insert_in_meta_table[implode('_', $key_in_db_format)] = $value_value_field;
}
$last_depth = $iterator->getDepth();
}
echo '<pre>';
print_r($values_to_insert_in_meta_table);
Maybe I missed something, but as far as I understand, I would do something like that:
<?php
function flatten(array $array, ?string $prefix = null): array {
$prefix = $prefix === null ? '' : "{$prefix}_";
$output = [];
foreach ($array as $key => $value) {
$key = $prefix . $key;
if (is_array($value)) {
$output = array_merge($output, flatten($value, $key));
} else {
$output[$key] = $value;
}
}
return $output;
}
var_export(flatten([
'key1' => 'foo',
'key2' => [
'key3' => [
0 => ['key4' => 'bar' ],
1 => ['key4' => 'azerty']
]
]
]));
Output:
array (
'key1' => 'foo',
'key2_key3_0_key4' => 'bar',
'key2_key3_1_key4' => 'azerty',
)
I think I've found a solution!!! :-)
$iterator = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($values), \RecursiveIteratorIterator::SELF_FIRST);
$key_in_db_format = [];
$current_counter = 0;
foreach($iterator as $value_key_field => $value_value_field) {
if(is_array($value_value_field)) {
$current_counter = 0;
array_push($key_in_db_format, $value_key_field);
}
if(!is_array($value_value_field)) {
$key_to_insert_in_db = !empty($key_in_db_format) ? implode('_', $key_in_db_format) . '_' . $value_key_field : $value_key_field ;
$values_to_insert_in_meta_table[$key_to_insert_in_db] = $value_value_field;
if($current_counter == count($iterator->getSubIterator())) {
array_pop($key_in_db_format);
}
$current_counter++;
}
}
echo '<br /> <pre>';
print_r($values_to_insert_in_meta_table);
exit;
The idea is:
We add to the array of ascendent keys the key if, and only if, the current element is not a leaf.
If the current element is a leaf, then we define the key equalled to the imploded ascendent keys PLUS (concatenation) the current element's key. Moreover we pop the array of ascendent keys if there are not following siblings elements.

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',
),
)

What is a better way to replace IDs in an array with their value counterpart?

I have the following array that includes id:
[Key1] => 1
[Key2] => 2, 3
I would like to replace these ids by their respective name from this second array:
[0] => Array
(
[ID] => 1
[Name] => Name1
)
[1] => Array
(
[ID] => 2
[Name] => Name2
)
[2] => Array
(
[ID] => 3
[Name] => Name3
The desired output:
[Key1] => Name1
[Key2] => Name2, Name3
I have the following code which works but I know this is not the right way. If anybody could let me know what would be a better way to achieve this, it would be greatly appreciated.
What my code looks like:
$var1 = explode(", ", $array1["Key1"]); // No need to explode in this example but "Key1" sometimes includes more than 1 ID
$var2 = explode(", ", $array1["Key2"]);
$array1["Key1"] = $var1 ; // This row is for the new array generated from "explode" to be a sub-array
$array1["Key2"] = $var2 ; // Same
for ($i = 0; $i < 83; $i++){
if($array1["Key1"][0] == $array2[$i]["ID"]){
$array1["Key1"][0] = $array2[$i]["Name"];
}
if($array1["Key1"][1] == $array2[$i]["ID"]){
$array1["Key1"][1] = $array2[$i]["Name"];
}
// (etc)
if($array1["Key2"][0] == $array2[$i]["ID"]){
$array1["Key2"][0] = $array2[$i]["Name"];
}
if($array1["Key2"][1] == $array2[$i]["ID"]){
$array1["Key2"][1] = $array2[$i]["Name"];
}
// (etc)
}
$var1 = implode(", ", $array1["Key1"]);
$var2 = implode(", ", $array1["Key2"]);
$array1["Key1"] = $var1 ;
$array1["Key2"] = $var2 ;
Just extract the ID and Name into a single-dimension and use it as search and replace parameters. We need to modify the IDs to search for and turn them into a pattern /\b$v\b/ where \b is a word boundary, so that 1 won't replace the 1 in 164 for example:
$replace = array_column($array2, 'Name', 'ID');
$search = array_map(function($v) { return "/\b$v\b/"; }, array_keys($replace));
$array1 = preg_replace($search, $replace, $array1);
You need to nest some loops. Here is a sample that should work:
//Processing Array
$arrayOne = array(
"Key1" => "1",
"Key2" => "2, 3");
//Lookup Array
$arrayTwo = array(
array(
"ID" => "1",
"Name" => "Name1"),
array(
"ID" => "2",
"Name" => "Name2"),
array(
"ID" => "3",
"Name" => "Name3"));
var_dump($arrayOne);
//Loop through all values in our original array
foreach($arrayOne as &$arrValue) {
//Split the value in the original array into another temporary array
//if there are multiple values.
$valueArray = explode(", ", $arrValue);
$outputArray = array();
foreach($valueArray as &$myValue) {
//Now do a lookup to replace each value
foreach($arrayTwo as &$lookupValue) {
//Find a match
if($myValue==$lookupValue["ID"]) {
$myValue = $lookupValue["Name"];
//We found the value we want, so let's break out of this loop
break;
}
}
//Append the value
array_push($outputArray, $myValue);
}
//Convert back to string
$arrValue= implode(", ", $outputArray);
}
var_dump($arrayOne);
There are improvements you could possibly make to this code if your incoming data was always sorted, but I imagine that is just the case for your sample above.
I have an approach to do this. You can make a try if you wish see here at:- https://eval.in/839823. I am using array_column to map the key=>value pair and then simple used foreach.
<?php
$main = ['Key1' => 1,'Key2' => '2, 3'];
$match = [
[
'ID' => 1,
'Name' => 'Name1'
],
[
'ID' => 2,
'Name' => 'Name2'
],
[
'ID' => 3,
'Name' => 'Name3'
]
];
$final_array=[];
$mapped = array_column($match, 'Name', 'ID');
foreach($main as $k=>$v){
$r = explode(',',$v);
if(count($r)>1){
$final_array[$k] = $mapped[$r[0]]. ", ".$mapped[intval($r[1])];
}else{
$final_array[$k] = $mapped[$r[0]];
}
}
print '<pre>';
//print_r($mapped);
print_r($final_array);
print '</pre>';
Output :
Array
(
[Key1] => Name1
[Key2] => Name2,Name3
)
Edit : As per comment of Josh Maag,
My code will only work if he only has a maximum of 2 values in Key2.
If Key3 contains "4,5,6" this code will leave the 6 untouched.
<?php
$main = ['Key1' => 1,'Key2' => '2,3','Key3' => '4,5,6'];
$match = [
[
'ID' => 1,
'Name' => 'Name1'
],
[
'ID' => 2,
'Name' => 'Name2'
],
[
'ID' => 3,
'Name' => 'Name3'
],
[
'ID' => 4,
'Name' => 'Name4'
],
[
'ID' => 5,
'Name' => 'Name5'
],
[
'ID' => 6,
'Name' => 'Name6'
]
];
$final_array=[];
$mapped = array_column($match, 'Name', 'ID');
foreach($main as $k=>$v){
$r = explode(',',$v);
if(count($r)>1){
$final_array[$k] = implode(',',array_map(function($key) use ($mapped){ return $mapped[$key]; }, array_values($r)));
}else{
$final_array[$k] = $mapped[$r[0]];
}
}
print '<pre>';
print_r($mapped);
print_r($final_array);
print '</pre>';
?>
See demo See here https://eval.in/839939
The core function that should be used for this task is preg_replace_callback(). Why? Because it is uniquely qualified to handle this operation in a single function call. It seems a tragedy to not use php functions for their designed purpose.
Beyond preg_replace_callback(), only array_column() is needed to prepare the $array2 data as a simple lookup array.
Code: (Demo)
$array1=["Key1"=>"1","Key2"=>"22, 4, 123"];
$array2=[["ID"=>"1","Name"=>"Name1"],["ID"=>"22","Name"=>"Name22"],["ID"=>"123","Name"=>"Name123"]];
$lookup=array_column($array2,'Name','ID'); // generate array: keys = IDs, vals = Names
$result=preg_replace_callback('/\d+/',function($m)use($lookup){return isset($lookup[$m[0]])?$lookup[$m[0]]:"*{$m[0]}*";},$array1);
var_export($result);
Output:
array (
'Key1' => 'Name1',
'Key2' => 'Name22, **4**, Name123',
)
There is no need to run any preparations (excluding $lookup) using additional loops or function calls.
This pattern will match all full ID numbers from each element in $array1 and process them individual. Each numeric match is sent to the anonymous callback function to receive its customized replacement string -- delivered by the $lookup data.
As an additional consideration, I have included an asterisk-wrapped replacement when an ID is not found in $lookup.

Parse through unordered multidimensional array finding only strings

I have a multidimensional array of undefined depth.
$array = array(
"foo" => "bar",
42 => 24,
"multi" => array(
"dimensional" => array(
"array" => "foo",
"something" => 42,
"something2" => [1,2,3]
)
)
);
I need to parse through it, find all the values that are plane text and save them in another array keeping the pathway. So I expect the final array be like this:
$array = array(
"foo" => "bar",
"multi" => array(
"dimensional" => array(
"array" => "foo"
)
)
);
At the moment I'm trying to use recurrent function
$this->printAll($array);
public function printAll($a)
{
if (!is_array($a)) {
echo $a, ' <br>'; // here we can check if it is string and add to the final array
return;
}
foreach($a as $i=>$v) {
$this->printAll($v);
echo $i;
}
}
Could someone help me to figure out how to keep indexes through iterations and put it in the final array.
<?php
function printAll($array, &$save)
{
foreach ($array as $key => $values)
{
if ( ! is_numeric($values))
{
if (is_array($values))
{
printAll($values, $save[$key]);
}
else
{
$save[$key] = $values;
}
}
}
if ( ! empty($save)) {
$save = array_filter($save);
}
}
$array = array(
"foo" => "bar",
42 => 24,
"multi" => array(
"dimensional" => array(
"array" => "foo",
"something" => 42,
"something2" => [1,2,3]
)
)
);
$save = array();
printAll($array, $save);
print_r($save);
Outputs: PHP
Array (
[foo] => bar
[multi] => Array (
[dimensional] => Array (
[array] => foo
)))

How do I reform this array into a differently structured array

I have an array that looks like this:
[0] => Array
(
[name] => typeOfMusic
[value] => this_music_choice
)
[1] => Array
(
[name] => myMusicChoice
[value] => 9
)
[2] => Array
(
[name] => myMusicChoice
[value] => 8
)
I would like to reform this into something with roughly the following structure:
Array(
"typeOfMusic" => "this_music_choice",
"myMusicChoice" => array(9, 8)
)
I have written the following but it doesn't work:
foreach($originalArray as $key => $value) {
if( !empty($return[$value["name"]]) ){
$return[$value["name"]][] = $value["value"];
} else {
$return[$value["name"]] = $value["value"];
}
}
return $return;
I've tried lots of different combinations to try and get this working. My original array could contain several sets of keys that need converting to arrays (i.e. it's not always going to be just "myMusicChoice" that needs converting to an array) ?
I'm getting nowhere with this and would appreciate a little help. Many thanks.
You just need to loop over the data and create a new array with the name/value. If you see a repeat name, then change the value into an array.
Something like this:
$return = array();
foreach($originalArray as $data){
if(!isset($return[$data['name']])){
// This is the first time we've seen this name,
// it's not in $return, so let's add it
$return[$data['name']] = $data['value'];
}
elseif(!is_array($return[$data['name']])){
// We've seen this key before, but it's not already an array
// let's convert it to an array
$return[$data['name']] = array($return[$data['name']], $data['value']);
}
else{
// We've seen this key before, so let's just add to the array
$return[$data['name']][] = $data['value'];
}
}
DEMO: https://eval.in/173852
Here's a clean solution, which uses array_reduce
$a = [
[
'name' => 'typeOfMusic',
'value' => 'this_music_choice'
],
[
'name' => 'myMusicChoice',
'value' => 9
],
[
'name' => 'myMusicChoice',
'value' => 8
]
];
$r = array_reduce($a, function(&$array, $item){
// Has this key been initialized yet?
if (empty($array[$item['name']])) {
$array[$item['name']] = [];
}
$array[$item['name']][] = $item['value'];
return $array;
}, []);
$arr = array(
0 => array(
'name' => 'typeOfMusic',
'value' => 'this_music_choice'
),
1 => array(
'name' => 'myMusicChoice',
'value' => 9
),
2 => array(
'name' => 'myMusicChoice',
'value' => 8
)
);
$newArr = array();
$name = 'name';
$value = 'value';
$x = 0;
foreach($arr as $row) {
if ($x == 0) {
$newArr[$row[$$name]] = $row[$$value];
} else {
if (! is_array($newArr[$row[$$name]])) {
$newArr[$row[$$name]] = array();
}
array_push($newArr[$row[$$name]], $row[$$value]);
}
$x++;
}

Categories