Split an array by key - php

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

Related

Create nested array/object from an array of strings by removing dots [duplicate]

I have a list of strings that each contain dot notated values. I want to convert this list into an associative array with each segment of the dot notation as a key in the appropriate nesting level. The deepest level of nesting should have a value of boolean true. There's no limit to the number or dot segments a string can contain so the code needs to do some kind of recursion to achieve the goal.
Input example:
[
'foo.bar',
'foo.bar.baz',
'foo.bar.qux',
'foo.qux',
'foo.quux',
'bar.baz',
'bar.qux',
'qux.quux',
]
Required output:
[
'foo' => [
'bar' => [
'baz' => true,
'qux' => true,
],
'qux' => true,
'quux' => true,
],
'bar' => [
'baz' => true,
'qux' => true,
],
'qux' => [
'quux' => true
]
]
foreach only solution.
function convert(array $input)
{
$r = [];
foreach ($input as $dotted) {
$keys = explode('.', $dotted);
$c = &$r[array_shift($keys)];
foreach ($keys as $key) {
if (isset($c[$key]) && $c[$key] === true) {
$c[$key] = [];
}
$c = &$c[$key];
}
if ($c === null) {
$c = true;
}
}
return $r;
}
If you call your starting array of strings $input, the $output array in the code below is what you want
$input=[...]; //original array
$output=[]; //holds the results;
foreach ($input as $line){
$keys = explode('.',$line);//break each section into a key
$val = true; //holds next value to add to array
$localArray = []; //holds the array for this input line
for($i=count($keys)-1; $i>=0; $i--){ //go through input line in reverse order
$localArray = [$keys[$i]=>$val]; //store previous value in array
$val = $localArray; //store the array we just built. it will be
//the value in the next loop
}
$output = array_merge_recursive($output,$localArray);
}
Live demo
You can do this using pointer and some logic.
<?php
$test = [
'foo.bar',
'foo.bar.baz',
'foo.bar.qux',
'foo.qux',
'foo.quux',
'bar.baz',
'bar.qux',
'qux.quux',
];
class Test
{
public $result = [];
public function check($array){
foreach ($array as $value) {
$this->change($value);
}
}
public function change($string)
{
$explodedValues = explode('.',$string);
$pointerVariable = &$this->result;
foreach ($explodedValues as $explodedValue) {
if(!is_array($pointerVariable)){
$pointerVariable = [];
$pointerVariable[$explodedValue] = true;
$pointerVariable = &$pointerVariable[$explodedValue];
} else if(isset($pointerVariable[$explodedValue])){
$pointerVariable = &$pointerVariable[$explodedValue];
} else {
$pointerVariable[$explodedValue] = true;
$pointerVariable = &$pointerVariable[$explodedValue];
}
}
}
}
$obj = new Test();
$obj->check($test);
print_r($obj->result);
Hope this will help
function convert($aa) {
$zz = [];
foreach( $aa as $value ) {
$bb = explode('.',$value);
$cc = count($bb);
for( $ii = 0, $yy = &$zz; $ii < $cc ; $ii++ ) {
$key = $bb[$ii];
if ( array_key_exists($key,$yy) === false || is_array($yy[$key]) === false ) {
$yy[$key] = [];
}
$yy = &$yy[$key];
}
$key = $bb[$cc-1];
if ( array_key_exists($key,$yy) === false ) {
$yy[$key] = true;
}
}
return $zz;
}

Convert a list of dot noted strings into an associative array (PHP)

I have a list of strings that each contain dot notated values. I want to convert this list into an associative array with each segment of the dot notation as a key in the appropriate nesting level. The deepest level of nesting should have a value of boolean true. There's no limit to the number or dot segments a string can contain so the code needs to do some kind of recursion to achieve the goal.
Input example:
[
'foo.bar',
'foo.bar.baz',
'foo.bar.qux',
'foo.qux',
'foo.quux',
'bar.baz',
'bar.qux',
'qux.quux',
]
Required output:
[
'foo' => [
'bar' => [
'baz' => true,
'qux' => true,
],
'qux' => true,
'quux' => true,
],
'bar' => [
'baz' => true,
'qux' => true,
],
'qux' => [
'quux' => true
]
]
foreach only solution.
function convert(array $input)
{
$r = [];
foreach ($input as $dotted) {
$keys = explode('.', $dotted);
$c = &$r[array_shift($keys)];
foreach ($keys as $key) {
if (isset($c[$key]) && $c[$key] === true) {
$c[$key] = [];
}
$c = &$c[$key];
}
if ($c === null) {
$c = true;
}
}
return $r;
}
If you call your starting array of strings $input, the $output array in the code below is what you want
$input=[...]; //original array
$output=[]; //holds the results;
foreach ($input as $line){
$keys = explode('.',$line);//break each section into a key
$val = true; //holds next value to add to array
$localArray = []; //holds the array for this input line
for($i=count($keys)-1; $i>=0; $i--){ //go through input line in reverse order
$localArray = [$keys[$i]=>$val]; //store previous value in array
$val = $localArray; //store the array we just built. it will be
//the value in the next loop
}
$output = array_merge_recursive($output,$localArray);
}
Live demo
You can do this using pointer and some logic.
<?php
$test = [
'foo.bar',
'foo.bar.baz',
'foo.bar.qux',
'foo.qux',
'foo.quux',
'bar.baz',
'bar.qux',
'qux.quux',
];
class Test
{
public $result = [];
public function check($array){
foreach ($array as $value) {
$this->change($value);
}
}
public function change($string)
{
$explodedValues = explode('.',$string);
$pointerVariable = &$this->result;
foreach ($explodedValues as $explodedValue) {
if(!is_array($pointerVariable)){
$pointerVariable = [];
$pointerVariable[$explodedValue] = true;
$pointerVariable = &$pointerVariable[$explodedValue];
} else if(isset($pointerVariable[$explodedValue])){
$pointerVariable = &$pointerVariable[$explodedValue];
} else {
$pointerVariable[$explodedValue] = true;
$pointerVariable = &$pointerVariable[$explodedValue];
}
}
}
}
$obj = new Test();
$obj->check($test);
print_r($obj->result);
Hope this will help
function convert($aa) {
$zz = [];
foreach( $aa as $value ) {
$bb = explode('.',$value);
$cc = count($bb);
for( $ii = 0, $yy = &$zz; $ii < $cc ; $ii++ ) {
$key = $bb[$ii];
if ( array_key_exists($key,$yy) === false || is_array($yy[$key]) === false ) {
$yy[$key] = [];
}
$yy = &$yy[$key];
}
$key = $bb[$cc-1];
if ( array_key_exists($key,$yy) === false ) {
$yy[$key] = true;
}
}
return $zz;
}

Accessing the nth element in multidimensional array

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

PHP - Mutidimensional array diff

i would like to ask for your help since I'm having difficulty resolving this matter. I had created a function to facilitate on array diff but it does not suffice to my needs. Thanks and more power!
<?php
$arraySession = array(
'sampleA' => array('1', '2', '3'),
'sampleB' => array('1', '2', '3'),
);
$arrayPost = array(
'sampleA' => array('1'),
'sampleB' => array('1','2'),
);
result should be:
array(
'sampleA' => array('2', '3')
'sampleB' => array('3'),
)
my existing function:
public function array_diff_multidimensional($session, $post) {
$result = array();
foreach($session as $sKey => $sValue){
foreach($post as $pKey => $pValue) {
if((string) $sKey == (string) $pKey) {
$result[$sKey] = array_diff($sValue, $pValue);
} else {
$result[$sKey] = $sValue;
}
}
}
return $result;
}
Any help would be much appreciated! Happy coding!
Not my function and not tested by me, but this was one of the first comments at php.net/array_diff (credit goes to thefrox at gmail dot com)
<?php
function multidimensional_array_diff($a1, $a2) {
$r = array();
foreach ($a2 as $key => $second) {
foreach ($a1 as $key => $first) {
if (isset($a2[$key])) {
foreach ($first as $first_value) {
foreach ($second as $second_value) {
if ($first_value == $second_value) {
$true = true;
break;
}
}
if (!isset($true)) {
$r[$key][] = $first_value;
}
unset($true);
}
} else {
$r[$key] = $first;
}
}
}
return $r;
}
?>
This should do it, assuming all keys occur in both arrays:
$diff = array();
foreach ($session as $key => $values) {
$diff[$key] = array_diff($values, $post[$key]);
}
Or, because I'm bored and array_map is underused:
$diff = array_combine(
array_keys($session),
array_map(function ($a, $b) { return array_diff($a, $b); }, $session, $post)
);
(Assumed well ordered arrays though.)
You want something more or less like this:
public function array_diff_multidimensional($arr1, $arr2) {
$answer = array();
foreach($arr1 as $k1 => $v1) {
// is the key present in the second array?
if (!array_key_exists($k1, $arr2)) {
$answer[$k1] = $v1;
continue;
}
// PHP makes all arrays into string "Array", so if both items
// are arrays, recursively test them before the string check
if (is_array($v1) && is_array($arr2[$k1])) {
$answer[$k1] = array_diff_multidimensional($v1, $arr2[$k1]);
continue;
}
// do the array_diff string check
if ((string)$arr1[$k1] === (string)$arr2[$k1]) {
continue;
}
// since both values are not arrays, and they don't match,
// simply add the $arr1 value to match the behavior of array_diff
// in the PHP core
$answer[$k1] = $v1;
}
// done!
return $answer;
}

How do I invert a multidimensional array in PHP

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. :-)

Categories