Split string into array key => value in php
+2-1+18*+7-21+3*-4-5+6x29
The above string is an example of the type of string I am trying to split into an array of key => value or something similar. A string is used to represent the layout of various classes on a three-column page on an intranet site that can be modified by the user using drag and drop. This string is stored in a cookie that will be used on the next visit.
The numbers represent the class id and -, + and x represent the class state (collapsed, expanded, or hidden), * represents a column break.
I can split this into columns easily using explode which gives arrays with 3 $ key => $ associations as well.
eg.
$column_layout = array( [0] => '+2-1+18' , [1] => '+7-21+3' , [2] => '-4-5+6x29' )
Then I need to split this into different classes keeping the status and id together. Since the different classes and statuses will change from user to user and how many for each column, I should be able to do this all automatically.
eg.
$column1 = array( '+' => 2 , '-' => 1 , '+' => 18 )
...
or
$column1 = array( array( '+' , 2 ) , array( '-' , 1 ) , array( '+' , 18 ) )
...
I can't say this is the best way to do this, so any help would be much appreciated.
a source to share
First, explode()
an array with the separator*
Then you can use preg_match_all to match each item in the exploded array. Something like this works with your example input.
$layout = explode('*', $input);
$columns = array();
foreach ( $layout as $item ){
$parts = array();
//matches either a -, x or + followed by one or more digits
preg_match_all('/([+-x])(\d+)/', $item, $matches, PREG_SET_ORDER);
foreach ( $matches as $match){
//match[1] hold the + or -, match[2] holds the digits
$parts[] = array($match[1], $match[2]);
}
$columns[] = $parts;
}
The result of your example ends like this:
array(
array( array('+', '2'), array('-', '1'), array('+', '18') ),
array( array('+', '7'), array('-', '21'), array('+', '3') ),
//etc
);
As of PHP 5.3, you can use something like this (untested). The main difference is that the inner loop has been replaced with array_map
, which eliminates the need for many lines of code. (An array map applies a function to each element of the array and returns the transformed array). PHP 5.3 is required for good closure syntax
$layout = explode('*', $input);
$columns = array();
foreach ( $layout as $item ){
preg_match_all('/([+-x])(\d+)/', $item, $matches, PREG_SET_ORDER);
$columns[] = array_map( function($a){ return array($a[1], $a[2]); },
$matches);
}
You can also remove loops completely:
$innerMatch = function($item){
preg_match_all('/([+-x])(\d+)/', $item, $matches, PREG_SET_ORDER);
return array_map( function($a){ return array($a[1], $a[2]); },
$matches);
};
$columns = array_map($innerMatch, explode('*', $input));
However, this has a big drawback: it is not very readable for most PHP developers, so I would not recommend using it.
Additional Information
As requested by @ Christopher Altman
The only new bit of PHP 5.3 is:
array_map(
function($a){ return array($a[1], $a[2]); },
$matches
);
Extending and modifying it a bit (as an example)
//bind an anonymous function to the variable $func
$func = function($a){
return $a*$a;
};
//$func() now calls the anonymous function we have just defined
//then we can call it like so:
$result = array_map($func, $myArray);
So, if $myArray
defined as
array(1,2,3,4);
When it is run through an array display function, you can think of it as converting it to
array(func(1),func(2),func(3),func(4));
But since PHP is not a lazy language , all functions are evaluated as soon as they are encountered, so the array is returned from array_map as:
array(2, 4, 9, 16)
In the actual code, it preg_match_all
returns an array of matches (where matches are arrays). So, all I do is take an array and in each match apply a function that converts the match to another array in the required format.
a source to share