Use array_column in combination with preg_match - php

Lets suppose we have an array of arrays that needs to be converted to rows
From this:
Array
(
[subject] => Array
(
[0] => EDN:LOC:DERR
[1] => EDN:LOC:DOXX
[2] => EDN:LOC:NTTT
[3] => EDN:LOC:NAGA
)
[object] => Array
(
[0] => ABS:D01::ADFPAZ01
[1] => ABS:D01::DOXYWITX
[2] => ABS:D01::NAGBAAD2
[3] => ABS:D01::NAGGAAD2
)
[units] => Array
(
[0] => ABS:D06::UNAA
[1] => ABS:D06::UMMM
[2] => ABS:D06::UPOP
[3] => ABS:D06::UPOP
)
To this:
[0] => "'DERR' , 'ADFPAZ01' , 'UNAA'"
[1] => "'DOXX' , 'DOXYWITX' , 'UMMM'"
[2] => "'NTTT' , 'NAGBAAD2' , 'UPOP'"
[3] => "'NAGA' , 'NAGGAAD2' , 'UPOP'"
So I need the arrays to be cleaned by a pattern and compressed into lines.
I managed the compact view with the following function
$array_res = array();
for ($i=0; $i<=$totalEntries-1; $i++) {
array_push($array_res, implode("', '", array_column($array_of_arrays, $i)));
}
My regex pattern is $pattern = '([^.:]*$)'; And it collects a sequence of letters from the end of the string until it finds a colon. And I used preg_match($pattern, $string, $match) to receive the proper string into the $match variable.
However, I cannot combine the above two procedures either with array_filter or array_map inside the for loop.
EDIT: Note that there can be a subarray that contains values without a colon. In that case we have to get the value as is
[units] => Array
(
[0] => NULL
[1] => VALUE1
[2] => VALUE2
[3] => NULL
)

Rather than using a regex, this just uses array_walk() to process the extracted column and for each item it uses strrchr() with : as the last character to match (although it will include the :, so uses substr() to remove the first char)...
for ($i=0; $i<=$totalEntries-1; $i++) {
$newRow = array_column($array_of_arrays, $i);
array_walk($newRow, function (&$data) {
$data = substr(strrchr(":".$data, ":") , 1);
});
$array_res[] = "'".implode("', '", $newRow)."'";
}
The part ":".$data deals with the time when there is no : in the string, it will always ensure that it does find something to use.

Other way:
$arr = [
'subject' => [ 'EDN:LOC:DERR', 'EDN:LOC:DOXX', 'EDN:LOC:NTTT', 'EDN:LOC:NAGA' ],
'object' => [ 'ABS:D01::ADFPAZ01', 'ABS:D01::DOXYWITX', 'ABS:D01::NAGBAAD2', 'ABS:D01::NAGGAAD2' ],
'units' => [ 'ABS:D06::UNAA', 'ABS:D06::UMMM', 'ABS:D06::UPOP', 'ABS:D06::UPOP' ]
];
$res = [];
$fmt = "'%s', '%s', '%s'";
foreach ($arr['subject'] as $k => $v) {
$res[] = vsprintf($fmt, preg_replace('~^.*:~', '', array_column($arr, $k)));
}
print_r($res);
Notice: If you don't know in advance your array length, nothing forbids to build the format pattern dynamically (using str_repeat or implode).

Related

PHP Explode string between two characters to arrays? (Noob question)

Hello :) I am a beginner in PHP.
I tried several times but did not succeed
I would like to parse a String like :
[1,[01,11,12],[20,21,22]]
to
`
arr[0][0]=>1
arr[1][0]=>01
arr[1][1]=>11
arr[1][2]=>12
arr[2][0]=>20
arr[2][1]=>21
arr[2][2]=>22
`
You can split your string on a comma that is not enclosed by [ and ] using this regex (inspired by this answer) with preg_split:
,(?![^\[]*\])
and then trim surrounding [ and ] from the resultant parts and split those strings on commas into succeeding elements of the output array. For example:
$string = '[1,[01,11,12] ,4 ,5, [20,21,22]]';
$parts = preg_split('/,(?![^\[]*\])/', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
$output = array();
foreach ($parts as $part) {
$part = trim($part, '[] ');
$output[] = explode(',', $part);
}
print_r($output);
Output:
Array
(
[0] => Array
(
[0] => 1
)
[1] => Array
(
[0] => 01
[1] => 11
[2] => 12
)
[2] => Array
(
[0] => 4
)
[3] => Array
(
[0] => 5
)
[4] => Array
(
[0] => 20
[1] => 21
[2] => 22
)
)
Demo on 3v4l.org
If you're 100% certain of the source and safety of the string, you can also just use eval:
eval("\$output = $string;");
The result will be the same.

Get all text outside parenthesis while capturing word index

How can I get all text that's not in parenthesis using preg_match_all? The reason I need to use preg_match_all is because I want to get the index of each word.
Given sentence:
Hello how [t- are] you [t- today], Sir?
I can extract all the words inside the ( ), which works. How can I also get all text outside the ( ) separately?
preg_match_all('/\[t-(.*?)\]/', $this->target, $targetWords, PREG_OFFSET_CAPTURE);
Output:
Array
(
[0] => Array
(
[0] => are
[1] => 47
),
[0] => Array
(
[0] => today
[1] => some number
)
)
Note: I already know about preg_split:
$outsideParenthesis = preg_split('/\[.*?\]/', $this->target);
But this doesn't allow me to maintain the index.
Note 2: It may help to provide my end goal:
I want to take a string of custom markdown. For each word, I want to generate word objects that specify their type and content.
The reason is, I'd like to send an array of word objects in order to the frontend so I can loop through the array and generate HTML elements with classes, so I can apply styling as needed.
And I want to be able to specify any markdown within, e.g.,
Hello how [t- are] you [k- today], Sir?
Where t- is target, k- is key.
So the final array I'd like would look like:
[
[
type => 'normal'
content => 'Hello how '
],
[
type => 'target'
content => 'are'
],
[
type => 'normal'
content => ' you'
]
[
type => 'key'
content => 'today'
]
[
type => 'normal'
content => ', Sir?'
]
]
Here's my wordObjects function as of now:
private function setWordObjects($array, $type)
{
return array_map(function ($n) use ($type) {
return [
'type' => $type,
'content' => $n[0],
'index' => $n[1]
];
}, $array[1]);
}
With preg_match_all
$str = 'Hello how [t- are] you [k- today], Sir?';
$types = ['' => 'normal', 't' => 'target', 'k' => 'key'];
if ( preg_match_all('~ (?| \[ (?<type>[^]-]+) - \h (?<content>[^]]+) ]
| () ([^[]+) ) ~x', $str, $matches, PREG_SET_ORDER) ) {
foreach ($matches as &$m) {
unset($m[0], $m[1], $m[2]);
$m['type'] = $types[$m['type']];
}
print_r($matches);
}
demo
Extended solution:
$s = 'Hello how [t- are] you [k- today], Sir?';
$types = ['t-' => 'target', 'k-' => 'key'];
$splitted = preg_split('/\[([tk]- [^]]+)\]/', $s, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_OFFSET_CAPTURE);
$result = [];
foreach ($splitted as $v) {
[$content, $pos] = $v;
$k = substr($content, 0, 2);
$is_delim = isset($types[$k]);
$result[] = array_combine(['type', 'content', 'index'],
[$is_delim? $types[$k] : 'normal',
$is_delim? substr($content, 3) : $content,
$is_delim? $pos + 3 : $pos]);
}
print_r($result);
The output:
Array
(
[0] => Array
(
[type] => normal
[content] => Hello how
[index] => 0
)
[1] => Array
(
[type] => target
[content] => are
[index] => 14
)
[2] => Array
(
[type] => normal
[content] => you
[index] => 18
)
[3] => Array
(
[type] => key
[content] => today
[index] => 27
)
[4] => Array
(
[type] => normal
[content] => , Sir?
[index] => 33
)
)

Convert string with two delimiters into flat associative array [duplicate]

This question already has answers here:
Explode a string to associative array without using loops? [duplicate]
(10 answers)
Closed 7 months ago.
I'm really have no idea about regex...
So I got stuck... Can anyone give me a solution with explanation of regex itself?
Here is my code:
$str = "id:521082299088|name:JOHNSON GREIG DENOIA|mounth:JAN17|amount:170027|admin:2500|billqty:1|metre:R1/900|usage:00010261-00010550|reffno:0BKP21851AF3EC2E0D4F56997EA19DFA|charge:170377|balace:1935";
$pregsplit = preg_split("/[\s|]+/",$string2);
Output:
Array
(
[0] => id:521082299088
[1] => name:JOHNSON
[2] => GREIG
[3] => DENOIA
[4] => mounth:JAN17
[5] => amount:170027
[6] => admin:2500
[7] => billqty:1
[8] => metre:R1/900
[9] => usage:00010261-00010550
[10] => reffno:0BKP21851AF3EC2E0D4F56997EA19DFA
[11] => charge:170377
[12] => balance:1935
)
I want output like this:
Array
(
"id" => 521082299088
"name" => "JOHNSON GREIG DENOIA"
"mount" => "JAN17"
"amount" => 170027
"admin" => 2500
"billqty" => 1
"metre" => "R1/900"
"usage" => "00010261-00010550"
"reffno" => "0BKP21851AF3EC2E0D4F56997EA19DFA"
"charge" => 170377
"balance" => 1935
)
1) The solution using preg_match_all function with specific regex pattern:
$str = "id:521082299088|name:JOHNSON GREIG DENOIA|mounth:JAN17|amount:170027|admin:2500|billqty:1|metre:R1/900|usage:00010261-00010550|reffno:0BKP21851AF3EC2E0D4F56997EA19DFA|charge:170377|balace:1935";
preg_match_all("/(\w+):([^|]+)/", $str, $matches, PREG_SET_ORDER);
$result = [];
foreach ($matches as $items) {
$result[$items[1]] = $items[2];
}
// $items[1] contains a "parameter" name captured by the first capturing group (\w+)
// $items[2] contains a "parameter" value captured by the second capturing group ([^|]+)
print_r($result);
The output:
Array
(
[id] => 521082299088
[name] => JOHNSON GREIG DENOIA
[mounth] => JAN17
[amount] => 170027
[admin] => 2500
[billqty] => 1
[metre] => R1/900
[usage] => 00010261-00010550
[reffno] => 0BKP21851AF3EC2E0D4F56997EA19DFA
[charge] => 170377
[balace] => 1935
)
(\w+) - matches all alphanumeric characters followed by :
([^|]+) - matches all characters excepting | which is delimiter
http://php.net/manual/en/function.preg-match-all.php
2) In addition to the first approach - using array_combine function(to combine all respective values from two capturing groups):
preg_match_all("/(\w+):([^|]+)/", $str, $matches);
$result = array_combine($matches[1], $matches[2]);
// will give the same result
3) The third alternative approach would be using explode() function:
$result = [];
foreach (explode("|", $str) as $items) {
$pair = explode(":", $items);
$result[$pair[0]] = $pair[1];
}
If you are unable to write regular expression.Here is a simple solution using explode() method.The explode() function breaks a string into an array.
<?php
$str = "id:521082299088|name:JOHNSON GREIG DENOIA|mounth:JAN17|amount:170027|admin:2500|billqty:1|metre:R1/900|usage:00010261-00010550|reffno:0BKP21851AF3EC2E0D4F56997EA19DFA|charge:170377|balace:1935";
$array = explode('|',$str);
foreach($array as $key=>$value){
$data = explode(':',$value);
$final[$data[0]] = $data[1];
}
print_r($final);
?>
Output:
Array
(
[id] => 521082299088
[name] => JOHNSON GREIG DENOIA
[mounth] => JAN17
[amount] => 170027
[admin] => 2500
[billqty] => 1
[metre] => R1/900
[usage] => 00010261-00010550
[reffno] => 0BKP21851AF3EC2E0D4F56997EA19DFA
[charge] => 170377
[balace] => 1935
)
To learn more about explode() read docs http://php.net/manual/en/function.explode.php
A funny way (only if your string doesn't contain = or &): translate pipes to ampersands and colons to equal signs, then parse it as an URL query with parse_str:
$str = "id:521082299088|name:JOHNSON GREIG DENOIA|mounth:JAN17|amount:170027|admin:2500|billqty:1|metre:R1/900|usage:00010261-00010550|reffno:0BKP21851AF3EC2E0D4F56997EA19DFA|charge:170377|balace:1935";
parse_str(strtr($str, ':|', '=&'), $result);
print_r($result);
demo
This approach would be an alternative.
You can separate string and create an array from it using PHP's explode() function. Then you can separate the 'key:value' structure using strpos() and substr() functions.
// input string
$str = "id:521082299088|name:JOHNSON GREIG DENOIA|mounth:JAN17|amount:170027|admin:2500|billqty:1|metre:R1/900|usage:00010261-00010550|reffno:0BKP21851AF3EC2E0D4F56997EA19DFA|charge:170377|balace:1935";
// make an array out of the string, split elements on each pipe character ('|')
$arr = explode('|', $str);
// create an output array to keep the results
$output = [];
// process the array
foreach ($arr as $item) {
// get delimiter
$separatorPos = strpos($item, ':');
// take the key part (The part before the ':')
$key = substr($item, 0, $separatorPos);
// take the value part (The part after the ':')
$value = substr($item, $separatorPos);
// push it into the output array
$output[$key] = $value;
}
// dump the output array
var_export($output);
Dump of the output array would be like follwing;
[
'id' => ':521082299088',
'name' => ':JOHNSON GREIG DENOIA',
'mounth' => ':JAN17',
'amount' => ':170027',
'admin' => ':2500',
'billqty' => ':1',
'metre' => ':R1/900',
'usage' => ':00010261-00010550',
'reffno' => ':0BKP21851AF3EC2E0D4F56997EA19DFA',
'charge' => ':170377',
'balace' => ':1935',
]

PHP return value within array

I am trying to retrieve all the first objects from an array, this is how my array looks like:
Array
(
[0] => lorem;1;banana
[1] => ipsum;2;apple
[2] => dolor;3;grapefruit
[3] => sit;4;pineapple;
[4] => amet;5;orange
)
I want it to return a certain value of that and store it in a variable, so that I can get all the fruit names, for example. Any way to do this?
You can use array_map:
$fruits = array_map(function($item) {
$arr = explode(';', $item);
return $arr[2];
}, $array);
var_dump($fruits);
Assuming the fruits always come last and may or may not have a trailing semicolon, you can use
preg_replace — Perform a regular expression search and replace
Example:
$data = [
'lorem;1;banana',
'ipsum;2;apple',
'dolor;3;grapefruit',
'sit;4;pineapple;',
'amet;5;orange'
];
$fruits = preg_replace('#.+;(.+?)[;]*$#', "$1", $data);
print_r($fruits);
The pattern means match everything up and including to a semicolon (greedy), then capture the content up to the end ending with or without a semicolon.
Output:
Array
(
[0] => banana
[1] => apple
[2] => grapefruit
[3] => pineapple
[4] => orange
)
You can use array_walk_recursive function and change your array item as per requirement.
use following code which will give you appropriate output:
$test = array(0 => 'lorem;1;banana', 1 => 'ipsum;2;apple', 2 => 'dolor;3;grapefruit',3 => 'sit;4;pineapple;',4 => 'amet;5;orange');
array_walk_recursive($test, 'test');
function test(&$item, $key)
{
$itemArray = explode(';', $item);
$item = $itemArray[2];
}
For the sake of completeness, those strings can be easily parsed as csv with str_getcsv
$a = array(
'lorem;1;banana',
'ipsum;2;apple',
'dolor;3;grapefruit',
'sit;4;pineapple;',
'amet;5;orange'
);
foreach($a as $line){
$csvString = str_getcsv($line,';');
$fruits[] = $csvString[2];
}
print_r($fruits);
Will output
Array
(
[0] => banana
[1] => apple
[2] => grapefruit
[3] => pineapple
[4] => orange
)

Looping through and clearing arrays

I have arrays within arrays, all with varying amounts of information. My CSV table currently has the fields Name, Email, and Phone Number.
Below is my array;
Array
(
[0] => Array
(
[0] => Name
[1] => Email
[2] => Phone Number
)
[1] => Array
(
[0] => Mick
[1] => mick#mick.com
[2] => 01234 324234
)
[2] => Array
(
[0] => james
[1] => james#james.com
[2] =>
)
[3] => Array
(
[0] => reg
[1] => reg#reg.com
[2] => 10293 467289
)
)
I wish to loop through and remove these null values and combine the Email and Phone Number into Info end up with an array which resembles
Array
(
[0] => Array
(
[0] => Name
[1] => Info
)
[1] => Array
(
[0] => Mick
[1] => mick#mick.com + 01234 324234
)
[2] => Array
(
[0] => james
[1] => james#james.com
)
[3] => Array
(
[0] => reg
[1] => reg#reg.com + 10293 467289
)
)
Here is my current script, I am recienving the error;
<b>Warning</b>: array_filter() expects parameter 2 to be a valid callback, no array or string given in <b>C:\Users\Lydia\Documents\XAMPP\htdocs\CSV.php</b> on line <b>21</b><br />
every time that I loop through the changeRow() function, any help greatly appreciated
index.php
<?php
include 'CSV.php';
header('Content-type: text/plain');
$file = read_csv('Book1.csv');
$input = changeRow($file);
CSV.php
....
....
function changeRow($rows){
$len = count($rows);
for($i = 0; $i < $len; $i++){
$rows = array_filter($rows[0],0);
}
}
Can use array_map() instead of foreach(). Example:
$file = read_csv('Book1.csv');
$input = array_map(function($v){
$phone = (isset($v[2]) && $v[2]) ? ' + '. $v[2] : '';
return array($v[0], $phone);
},$file );
if(isset($result[0][1])) $result[0][1] = 'Info';
print '<pre>';
print_r($input);
print '</pre>';
I'll provide two methods that output the requested array structure. (PHP Demo Link) These methods iterate the array, check if the iteration is dealing with the "column heading" subarray or not, then conditionally appending the value from subarray element [2] to subarray element [1] using + as glue.
Method #1: foreach()
foreach($array as $index=>$item){
if(!$index){
$result[]=['Name','Info'];
}else{
$result[]=[$item[0],$item[1].(strlen($item[2])?" + $item[2]":'')];
}
}
var_export($result);
Method #2 array_map()
var_export(
array_map(function($index,$item){
if(!$index){
return ['Name','Info'];
}else{
return [$item[0],$item[1].(strlen($item[2])?" + $item[2]":'')];
}
},array_keys($array),$array)
);
Output: (from either method)
array (
0 =>
array (
0 => 'Name',
1 => 'Info',
),
1 =>
array (
0 => 'Mick',
1 => 'mick#mick.com + 01234 324234',
),
2 =>
array (
0 => 'james',
1 => 'james#james.com',
),
3 =>
array (
0 => 'reg',
1 => 'reg#reg.com + 10293 467289',
),
)
ps. If you want to remove the $index==0 check that is iterated each time, you can manually overwrite the first subarray after the loop is finished like this: (PHP Demo Link) *this just means you will be "writing" data to the first subarray twice.
foreach($array as $item){
$result[]=[$item[0],$item[1].(strlen($item[2])?" + $item[2]":'')];
}
$result[0]=['Name','Info'];
var_export($result);
or
$result=array_map(function($item){return [$item[0],$item[1].(strlen($item[2])?" + $item[2]":'')];},$array);
$result[0]=['Name','Info'];
var_export($result);
pps. "Passing by Reference" can be used for this task, but I've elected not to use &$array because it can risk causing trouble "downscript" and many developers advise against using it until other methods are inadequate. Here is what that can look like: (PHP Demo Link)
foreach($array as &$item){
if(strlen($item[2])) $item[1].=" + $item[2]";
unset($item[2]);
}
$array[0]=['Name','Info'];
var_export($array);
unset($item); // var_export($item); // ($item = NULL)

Categories