My intent is to take a series of strings, explode them by "_" and use the keys from that array to build a "master" array.
My rules are:
if key no exists -> create key under parent array
if key exists -> add key as new array to parent
if key is last -> add value
String Examples
total_players_count
total_rosters_count
players_season1_count
players_season2_count
rosters_season1_count
Expected Results
$main = array(
'total' => array(
'players' => array(
'count' => '123',
'rosters' => array(
'count' => '123')
)
),
'players' => array(
'season1' => array(
'count' => '123'
)
)
);
I hope this shows what I've expected to write. Now let me lead into how I disappointed myself through a series of failures last night ;)
The gist is, my recursive functions haven't been working so well so I'm not posting any of that code. I've been working with array_key_exists and that isn't getting me my expected results. I came close using prev(), current(), and end() but the issue stems from an unexpected number of keys I have to parse through (otherwise I would just loop 3 times and be done. I know the following would work if I had a limited amount of keys to parse; but I don't.
<?php
private function _get_section($parent_key, $sql) {
$data = array();
$data[$parent_key] = array();
foreach ($sql AS $key => $value) {
$keys = explode('_', $key);
if ($keys[0] == $parent_key) {
$i = 0;
$total_keys = count($keys);
for ($k = 1; $k < $total_keys; $k++) {
$i++;
if ($i == 1) {
echo '1. (' . $i . ') ' . $keys[$k];
$data[$parent_key][$keys[$k]] = array();
}
else if ($i > 1 && $i < $total_keys - 1) {
$data[$parent_key][$keys[$k - 1]][$keys[$k]] = array();
}
else if ($i == $total_keys - 1) {
$tmp = array_reverse($data);
// can't get the last key because I need to recursively loop
// through the results to find where to set this last
// key / value
}
}
$k = 0;
$i = 0;
}
$keys = array();
$total_keys = 0;
}
}
Solution (thank you)
$tree = array();
foreach ($sql AS $key => $value) {
$parts = explode('_', $key);
$val = $value;
foreach (array_reverse($parts) AS $part) {
$val = array($part => $val);
}
$tree = array_merge_recursive($tree, $val);
}
untested. hopefully i didnt need to read your code to fully understand what you want, because i didnt. i just looked at the expected results + the input, although i wonder what happened to rosters_season1_count?
$tree = array();
foreach ($strings as $string) {
$parts = explode('_', $string);
$path = '123';
foreach (array_reverse($parts) as $part) {
$path = array($part => $path);
}
$tree = array_merge_recursive($tree, $path);
}
Related
I have a string like this:
$config = 'app.name = "My App";
app.id = "myappid";
app.components.cache.id = "Memcache";';
I need to convert it to array like this:
app=>[
'name=>'My App',
'id'=>'myappid',
'components'=>[
'cache'=>['id'=>'Memcache']
]
]
I use explode for divide on the pieces, but i don't know what i need to do next.
$arr = explode(";", $config);
$data = [];
foreach ($arr as $item) {
$pieces = explode('=', $item);
$pieces_dotes = explode('.', $pieces[0]);
foreach ($pieces_dotes as $piece) {
//what i need to do here?
}
}
How about this? Within the inner foreach loop, you're assigning the temp variable by reference to itself, so you build a stack of array keys, and then assign the value to it at the end.
$config = 'app.name = "My App";
app.id = "myappid";
app.components.cache.id = "Memcache";';
$arr = explode(";", $config);
$data = [];
foreach ($arr as $item) {
$temp = &$data;
$item = trim($item);
if (empty($item)) continue;
list($setting, $value) = explode("=", trim($item));
$setting = explode(".", trim($setting));
foreach ($setting as $set) {
$temp = &$temp[$set];
}
$temp = trim($value, '" ');
}
print_r($data);
This can be solved by building the array levels based on the key and keeping a reference to the last added level in the array. My solution (based on what you already had) is as follows:
<?php
$config = 'app.name = "My App";
app.id = "myappid";
app.components.cache.id = "Memcache";';
$arr = explode(";", $config);
$data = [];
foreach ($arr as $item) {
$pieces = explode('=', $item);
$currentValue = trim(str_replace('"', '', $pieces[1]));
$pieces_dotes = explode('.', $pieces[0]);
// Set initial value of $currentLevel to root of data array
$currentLevel = &$data;
// Initiate count & iterator vars
$numPieces = count($pieces_dotes);
$i = 0;
foreach ($pieces_dotes as $piece) {
// Increment $i and set $newKey to be trimmed version of current piece of the dot notated level
$i++;
$newKey = trim($piece);
// Skip empty $newKey
if(empty($newKey)) {
continue;
}
// Assign the $currentValue once we are at the required depth
if($i == $numPieces) {
$currentLevel[$newKey] = $currentValue;
}
// We are not at the required depth yet so initiate the key as an array if required
if(empty($currentLevel[$newKey])) {
$currentLevel[$newKey] = [];
}
// Set the $currentLevel to reference the new key
if(is_array($currentLevel[$newKey])) {
$currentLevel = &$currentLevel[$newKey];
}
}
}
Hope this helps!
The solution using custom recursive function fillBypath:
/**
* Fills an array by hierarchical 'key path'
*
* #param type $value value in each 'key path'
* #param type $keys hierarchical key list
* #param type $arr the resulting array(passed by reference)
*/
function fillByPath($value, $keys, &$arr) {
$c = count($keys) - 1;
foreach ($keys as $idx => $k) {
if ($idx == $c) { // check if it's the last key in the "key path"
$arr[$k] = $value;
break;
}
if (isset($arr[$k]) && is_array($arr[$k])) {
fillByPath($value, array_slice($keys, $idx + 1), $arr[$k]);
break;
} else {
$arr[$k] = [];
fillByPath($value, array_slice($keys, $idx + 1), $arr[$k]);
break;
}
}
}
$config = 'app.name = "My App";
app.id = "myappid";
app.components.cache.id = "Memcache";';
$result = [];
foreach (array_filter(explode(";", $config)) as $path) {
list($keyPath, $v) = explode(" = ", trim($path));
$keys = explode(".", $keyPath); // getting key list
fillByPath($v, $keys, $result);
}
print_r($result);
The output:
(
[app] => Array
(
[name] => "My App"
[id] => "myappid"
[components] => Array
(
[cache] => Array
(
[id] => "Memcache"
)
)
)
)
I have an array with the following keys:
Array
{
[vegetable_image] =>
[vegetable_name] =>
[vegetable_description] =>
[fruit_image] =>
[fruit_name] =>
[fruit_description] =>
}
and I would like to split them based on the prefix (vegetable_ and fruit_), is this possible?
Currently, I'm trying out array_chunk() but how do you store them into 2 separate arrays?
[vegetables] => Array { [vegetable_image] ... }
[fruits] => Array { [fruit_image] ... }
This should work for you:
$fruits = array();
$vegetables = array();
foreach($array as $k => $v) {
if(strpos($k,'fruit_') !== false)
$fruits[$k] = $v;
elseif(strpos($k,'vegetable_') !== false)
$vegetables[$k] = $v;
}
As an example see: http://ideone.com/uNi54B
Out of the Box
function splittArray($base_array, $to_split, $delimiter='_') {
$out = array();
foreach($to_split as $key) {
$search = $key.delimiter;
foreach($base_array as $ok=>$val) {
if(strpos($ok,$search)!==false) {
$out[$key][$ok] = $val;
}
}
return $out;
}
$new_array = splittArray($array,array('fruit','vegetable'));
It is possible with array_reduce()
$array = ['foo_bar' => 1, 'foo_baz' => 2, 'bar_fee' => 6, 'bar_feo' => 9, 'baz_bee' => 7];
$delimiter = '_';
$result = array_reduce(array_keys($array), function ($current, $key) use ($delimiter) {
$splitKey = explode($delimiter, $key);
$current[$splitKey[0]][] = $key;
return $current;
}, []);
Check the fiddle
Only one thins remains: you are using different forms (like "vegetable_*" -> "vegetables"). PHP is not smart enough to substitute language (that would be English language in this case) transformations like that. But if you like, you may create array of valid forms for that.
Use explode()
$arrVeg = array();
$arrFruit = array();
$finalArr = array();
foreach($array as $k => $v){
$explK = explode('_',$k);
if($explK[0] == 'vegetable'){
$arrVeg[$k] = $v;
} elseif($explK[0] == 'fruit') {
$arrFruit[$k] = $v;
}
}
$finalArr['vegetables'] = $arrVeg;
$finalArr['fruits'] = $arrFruit;
Use simple PHP array traversing and substr() function.
<?php
$arr = array();
$arr['vegetable_image'] = 'vegetable_image';
$arr['vegetable_name'] = 'vegetable_name';
$arr['vegetable_description'] = 'vegetable_description';
$arr['fruit_image'] = 'fruit_image';
$arr['fruit_name'] = 'fruit_name';
$arr['fruit_description'] = 'fruit_description';
$fruits = array();
$vegetables = array();
foreach ($arr as $k => $v) {
if (substr($k, 0, 10) == 'vegetable_') {
$vagetables[$k] = $v;
}
else if (substr($k, 0, 6) == 'fruit_') {
$fruits[$k] = $v;
}
}
print_r($fruits);
print_r($vagetables);
Working Example
is there a simple way to access the nth element in a multidimensional array in php?
so for example
$arr = array(
[0] => array(1,4,7,3,53),
[6] => array(6,3,9,12,51,7),
[2] => array(9,94,54,3,87));
the 12th element would be 9.
array keys are not necessarily in order, nor each array row is of the same length.
Try this :
<?php
$arr = array(
'0'=> array(1,4,7,3,53),
'6'=>array(6,3,9,12,51,7),
'2'=>array(9,94,54,3,87)
);
$newArray=array();
foreach($arr as $array){
$newArray=array_merge($newArray, $array);
}
echo $newArray[11];
?>
untested, should work...
$arr = array(
0 => array(1,4,7,3,53), // your code was wrong here
6 => array(6,3,9,12,51,7),
2 => array(9,94,54,3,87));
function getnth ($array, $offset) {
$tmp_arr = array();
foreach ($array as $key => $value) {
foreach ($value as $val) {
$tmp_arr[] = $val;
}
}
return (isset($tmp_arr[$offset -1]) ? $tmp_arr[$offset -1] : FALSE);
}
getnth($arr, 12);
Edit: got to admit, the array_merge version is better....
Edit2: this is probably faster, if performance is an issue....
function getnth($array, $offset) {
$i = 0;
foreach ($array as $key => $value){
$size = count($value);
$i += $size;
if($offset <= $i) {
$new_off = $size - ($i - $offset) -1 ;
return $value[$new_off];
}
}
return FALSE;
}
function get_item($arr, $path, $delim = '.') {
$path = explode($delim, $path);
$result = $arr;
foreach ($path as $item) {
if (isset($result[$item])) {
$result = $result[$item];
} else {
return null;
}
}
return $result;
}
using:
echo get_item($arr, 'item.value.3.4.2.etc');
Hi i have a multidimensional associative array:
$array= array(
'Book1' => array('http://www.google.com', '45' ),
'Book2' => array('http://www.yahoo.com', '46', )
)
I need to be able to search $array on 'BookX' and then return the contents of 'BookX'.
Ive tried:
function array_searcher($needles, $array)
{
foreach ($needles as $needle) {
foreach ($array as $key )
{
if ($key == $needle)
{
echo $key;
}
}
}
}
with the search
$needles = array('Book1' , 'Book2' );
But this doesnt return anything
I might be misunderstanding, but this just sounds like the accessor. If not, could you clarify?
$array= array(
'Book1' => array('http://www.google.com', '45' ),
'Book2' => array('http://www.yahoo.com', '46', )
);
echo $array['Book1'];
EDIT: I did misunderstand your goal. I do have a comment on doing the two foreach loops. While this does work, when you have a very large haystack array, performance suffers. I would recommend using isset() for testing if a needle exists in the haystack array.
I modified the function to return an array of the found results to remove any performance hits from outputing to stdout. I ran the following performance test and while it might not do the same search over and over, it points out the inefficiency of doing two foreach loops when your array(s) are large:
function array_searcher($needles, $array) {
$result = array();
foreach ($needles as $needle) {
foreach ($array as $key => $value) {
if ($key == $needle) {
$result[$key] = $value;
}
}
}
return $result;
}
function array_searcher2($needles, $array) {
$result = array();
foreach ($needles as $needle) {
if (isset($array[$needle])) {
$result[$needle] = $array[$needle];
}
}
return $result;
}
// generate large haystack array
$array = array();
for($i = 1; $i < 10000; $i++){
$array['Book'.$i] = array('http://www.google.com', $i+20);
}
$needles = array('Book1', 'Book2');
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
array_searcher($needles, $array);
}
echo (microtime(true) - $start)."\n";
// 14.2093660831
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
array_searcher2($needles, $array);
}
echo (microtime(true) - $start)."\n";
// 0.00858187675476
If you want to search using the keys, you should access it as a key => value pair.
If you don't it only retrieves the value.
function array_searcher($needles, $array)
{
foreach ($needles as $needle)
{
foreach ($array as $key => $value)
{
if ($key == $needle)
{
echo $key;
}
}
}
}
Whats the easiest way to invert a multidimensional array. By invert I mean similar to array_flip.
e.g
[0][5][var_name] = data
[0][3][var_name2] = data2
[1][var_name3] = data3
[0][1][4][var_name4] = data4
inverted would be:
[var_name][0][5] = data
[var_name2][0][3] = data2
[var_name3][1] = data3
[var_name4][0][1][4] = data4
Any ideas?
You could store a list of the keys of all ancestors and when you hit a "leaf" use this list to create the "flipped" version.
<?php
$data = array(
0=>array(
5=>array('var_name' => 'data'),
3=>array('var_name2' => 'data2'),
1=>array(4=>array('var_name4'=>'data4'))
),
1=>array('var_name3'=>'data3')
);
$result = array();
foo($data, $result);
($result['var_name'][0][5] === 'data') or die('1');
($result['var_name2'][0][3] === 'data2') or die('2');
($result['var_name3'][1] === 'data3') or die('3');
($result['var_name4'][0][1][4] === 'data4') or die('4');
echo 'ok';
function foo(array $a, array &$target, $stack=array()) {
foreach($a as $key=>$value) {
if ( is_array($value) ) {
array_push($stack, $key);
foo($value, $target, $stack);
array_pop($stack);
}
else {
$target[$key] = array();
$tmp = &$target[$key];
foreach( $stack as $s ) { // now it's not so stack-ish anymore :-S
$tmp[$s] = array();
$tmp = &$tmp[$s];
}
$tmp = $value;
}
}
}
I couldn't think of an easy way to do this. So I wrote up a really complicated way to do it. Namely:
Take the multidimensional array and flatten it into a list of keys and values.
Reverse the keys.
Unflatten the list to obtain an inverted multidimensional array.
Code
<?php
function print_entries($array, $prekeys = array())
{
foreach ($array as $key => $value)
{
$keys = array_merge($prekeys, array($key));
if (is_array($value))
print_entries($value, $keys);
else
echo '[' . implode('][', $keys) . "] = $value\n";
}
}
function flatten_array($array)
{
$entries = array();
foreach ($array as $key => $value)
{
if (is_array($value))
{
foreach (flatten_array($value) as $subentry)
{
$subkeys = $subentry[0];
$subvalue = $subentry[1];
$entries[] = array(array_merge(array($key), $subkeys), $subvalue);
}
}
else
$entries[] = array(array($key), $value);
}
return $entries;
}
function unflatten_array($entries)
{
$array = array();
foreach ($entries as $entry)
{
$keys = $entry[0];
$value = $entry[1];
$subarray = &$array;
foreach ($keys as $i => $key)
{
if ($i < count($keys) - 1)
{
if (!isset($subarray[$key]))
$subarray[$key] = array();
$subarray = &$subarray[$key];
}
else
$subarray[$key] = $value;
}
}
return $array;
}
function invert_array($array)
{
$entries = flatten_array($array);
foreach ($entries as &$entry)
$entry[0] = array_reverse($entry[0]);
return unflatten_array($entries);
}
$array = array
(
0 => array
(
5 => array('var_name' => 'data'),
3 => array('var_name2' => 'data2'),
1 => array(4 => array('var_name4' => 'data4'))
),
1 => array(0 => array('var_name' => 'data3'))
);
print_entries($array);
echo "\n";
print_entries(invert_array($array));
?>
Output
[0][5][var_name] = data
[0][3][var_name2] = data2
[0][1][4][var_name4] = data4
[1][0][var_name] = data3
[var_name][5][0] = data
[var_name2][3][0] = data2
[var_name4][4][1][0] = data4
[var_name][0][1] = data3
Edit: I noticed now that you don't reverse the keys but you simply move the var_name portion from the end to the front and leave the numerical indices alone. It's easy enough to modify the line in flatten_array where I call array_reverse to re-order the keys in a different way. The core flatten/unflatten logic would not need to be changed. I leave this as an exercise for the reader. :-)