Reputation: 405
I'm trying to take data that is received from form input and, in PHP, change the way it is ordered for use in a template creation module/app. I need it to be in a multidimensional array for use.
How the array currently looks
$templateData = [
0 => 'h',
1 => 1,
2 => 2,
3 => 3,
4 => 'c-1-3',
5 => 3,
6 => 5,
7 => 'c-2-3',
8 => 3,
9 => 'c-3-3',
10 => 'f',
11 => 2
];
How the array should look
$templateData = [
0 =>[ //header row
0 => [1,2,3] //column
],
1 =>[ //content row(s)
0 => [3,5], //column
1 => [3],
2 => [0]
],
2 => [ //footer row
0 => [2] //column
]
]
The 'h' represents the start of the header row of the template and 'c-1-3' represents a the start of a row with 3 columns starting with the first column. The 'f' represents the start of the footer row. I'm just drawing a blank right now and I can't wrap my head around it.
Here is where I'm at right now, but it's still not working as intended:
$elements = $request->input('elements'); //laravel code that grabs array input
$row = 0;
$column = 0;
foreach($elements as $key => $element) {
//if value is a letter
if(preg_match('/^[a-zA-Z]/', $element)) {
//if the 3rd letter in the string is a 1
if(!isset($element[2]) || $element[2] == '1') { $row++; }
//cycle through values after current key
for($i=$key+1; $i < count($elements);$i++){
//until you hit another letter
if(preg_match('/^[a-zA-Z]/', $elements[$i])) { break; }
$column++;
$temp[$row][$column][] = $elements[$i];
}
$column = 0;
}
}
The $templateData and $elements variables are the same in the the above and below context.
Upvotes: 0
Views: 118
Reputation: 48100
You won't need to use arithmetic to appropriately target the indexes of the multidimensional result array if you push data into reference variables.
The first level keys will be dictated by the leading non-hyphen substring of non-integer values.
The second level keys will be dictated by the whole non-integer value. This provides accurate grouping.
Code: (Demo)
$result = [];
foreach ($templateData as $v) {
if (!is_int($v)) {
if (isset($k2) && empty($ref2[$k2])) {
$ref2[$k2][] = 0; // set default 0 if previous group received no values
}
$k2 = $v; // cache the level 2 value
$k1 = strtok($v, '-'); // cache the level 1 value
if (!isset($ref1[$k1])) {
$ref1[$k1] = [];
$result[] =& $ref1[$k1]; // push level 1 reference into result array
}
if (!isset($ref2[$k2])) {
$ref2[$k2] = [];
$ref1[$k1][] =& $ref2[$k2]; // push level 2 reference into level 1 reference
}
} elseif (isset($k2)) {
$ref2[$k2][] = $v; // push integer into level 2 reference
}
}
var_export($result);
Output:
array (
0 =>
array (
0 =>
array (
0 => 1,
1 => 2,
2 => 3,
),
),
1 =>
array (
0 =>
array (
0 => 3,
1 => 5,
),
1 =>
array (
0 => 3,
),
2 =>
array (
0 => 0,
),
),
2 =>
array (
0 =>
array (
0 => 2,
),
),
)
Upvotes: -2
Reputation: 405
This is the code I ended up with. Finally got it. This works:
//grab input
$elements = $request->input('elements');
//initiate row/column numbers
$row = 0;
$column = 0;
$templateData = array();
//loop through each array value
foreach($elements as $key => $element) {
//if value starts with a letter
if(preg_match('/^[a-z]/', $element)){
//set total columns
if($element != 'h' && $element != 'f')
{
$totalColumns = substr($element, 4);
} else {
$column = 1;
$totalColumns = 1;
}
//if h, c-1-*, or f is detected advance the row
if(substr($elements[$key], 0) == 'h' || substr($elements[$key], 2) == 1 || substr($elements[$key], 0) == 'f')
{
$row++;
}
//loop through keys following first recognized letter until you hit another letter
for($i = $key+1; $i < count($elements) && !preg_match('/^[a-z]/',$elements[$i]); $i++)
{
//add element id to column
$templateData[$row-1][$column-1][] = $elements[$i];
}
//if first value in column is not set, add a 0 value to column
if(!isset($templateData[$row-1][$column-1][0]))
{
$templateData[$row-1][$column-1][0] = 0;
}
//if its the last column in the set, set to 0
if($column == $totalColumns)
{
$column = 0;
}
$column++;
}
}
dd($templateData);
Upvotes: 1
Reputation: 791
It's a bit overkill but you can have full power, and you just need to change the handlers if you want to change the logic. Hope it can help somehow.
$templateData = [
0 => 'h',
1 => 1,
2 => 2,
3 => 3,
4 => 'c-1-3',
5 => 3,
6 => 5,
7 => 'c-2-3',
8 => 3,
9 => 'f',
10 => 2
];
$tmpKey = '';
$tmpArray = [];
class Test
{
/**
* @var []
*/
private $data = [];
/**
* @var []
*/
private $columns = [];
/**
* @var array
*/
private $numOfColumns = 0;
/**
* @var []
*/
private $header = [];
/**
* @var []
*/
private $footer = [];
/**
* @var string
*/
private $current = [
'key' => '',
'matches' => ''
];
/**
* @var []
*/
private $rules = [
'header' => [
'expression' => 'h',
'handler' => 'headerHandler',
'matches' => false
],
'footer' => [
'expression' => 'f',
'handler' => 'footerHandler',
'matches' => false
],
'columns' => [
'expression' => 'c-\d+-\d+',
'handler' => 'columnHandler',
'matches' => "columnMatches"
]
];
/**
* @param $data
*/
public function __construct($data)
{
$this->data = $data;
$this->process();
}
/**
* @throws Exception
*/
private function process()
{
foreach($this->data as $data) {
if (!is_int($data)) {
if (!$this->setRule($data)) {
throw new \RuntimeException("Rule Not found.");
}
} else {
$this->handleRule($data);
}
}
}
/**
* @param $data
*/
private function headerHandler($data)
{
$this->header[] = $data;
}
/**
* @param $data
*/
private function footerHandler($data)
{
$this->footer[] = $data;
}
/**
* @param $data
*/
private function columnHandler($data)
{
$columns = explode('-', $this->current['matches']);
if ($this->columns[($columns[1] - 1)][0] == 0){
$this->columns[($columns[1] - 1)][0] = $data;
} else {
array_push($this->columns[($columns[1] - 1)], $data);
}
}
/**
* @param $data
*/
private function columnMatches($data)
{
$columns = explode('-', $data);
if ($this->numOfColumns == 0) {
$this->numOfColumns = end($columns);
$this->columns = array_fill(0, $this->numOfColumns, [0]);
}
}
/**
* @param $data
* @return bool
*/
private function setRule($data)
{
foreach($this->rules as $key => $rules) {
if (preg_match("/^{$rules['expression']}$/", $data, $matches)) {
if ($rules['matches']) {
$this->{$rules['matches']}($data);
}
$this->current['key'] = $key;
$this->current['matches'] = $data;
return true;
}
}
return false;
}
/**
* @return mixed
*/
public function getHeader()
{
return $this->header;
}
/**
* @return mixed
*/
public function getColumns()
{
return $this->columns;
}
/**
* @return mixed
*/
public function getFooter()
{
return $this->footer;
}
/**
* @return array
*/
public function getResult()
{
return [
0 => [$this->header],
1 => $this->columns,
2 => $this->footer
];
}
/**
* @param $data
*/
private function handleRule($data)
{
$this->{$this->rules[$this->current['key']]['handler']}($data);
}
}
$test = new Test($templateData);
echo '<h1>Header</h1>';
var_dump($test->getHeader());
echo '<h1>Footer</h1>';
var_dump($test->getFooter());
echo '<h1>Columns</h1>';
var_dump($test->getColumns());
echo '<h1>Result</h1>';
var_dump($test->getResult());exit;
Ideone: Example Here
Upvotes: 0