I am practising with the AdventureWorks database for now and I will be receiving strings like the following: SalesOrderNumber=SOH123 and CustomerID=1. The strings may not always contain =, as they may be >, <, >=, <=, !=, <>.
Ideally, I would like to split each string into 3 fields - the database column to query, the comparison (e.g. =, >, !=, etc) and the value to search for.
I can achieve what I want with lots of code, comparing character by character, but I am hoping someone can suggest a really simple way to do this, as I am sure this must be a fairly common task for websites.
I don't want to just use the strings as they come through, as I need to sanitise them first. I also don't want to send bad queries to the database that may generate SQL errors.
It is indirect and feasibly inefficient to make calls of explode() within nested loops with break condition.
I recommend preg_split() as the right tool for the job. Create a capture group containing a character class of whitelisted symbols. Allow a maximum of 3 elements as its return value. Use the PREG_SPLIT_DELIM_CAPTURE flag to inform the function to preserve the delimiting comparison operators when exploding the string into three parts.
Code: (Demo)
var_export(
array_map(
fn($v) => preg_split(
'/([!<=>]+)/',
$v,
3,
PREG_SPLIT_DELIM_CAPTURE
),
$array
)
);
Output:
array (
0 =>
array (
0 => 'SalesOrderNumber',
1 => '=',
2 => 'SOH123',
),
1 =>
array (
0 => 'CustomerID',
1 => '=',
2 => '1',
),
2 =>
array (
0 => 'BigOrEqual',
1 => '>=',
2 => '44',
),
3 =>
array (
0 => 'SmallOrEqual',
1 => '<=',
2 => '67',
),
4 =>
array (
0 => 'NotEqual',
1 => '!=',
2 => '123',
),
5 =>
array (
0 => 'NotEqual',
1 => '<>',
2 => '2000',
),
6 =>
array (
0 => 'Smaller',
1 => '<',
2 => '21',
),
7 =>
array (
0 => 'Bigger',
1 => '>',
2 => '12',
),
)
This is my aproach using foreach, explode and array functions:
$strings = [
'SalesOrderNumber=SOH123',
'CustomerID=1',
'BigOrEqual>=44',
'SmallOrEqual<=67',
'NotEqual!=123',
'NotEqual<>2000',
'Smaller<21',
'Bigger>12',
];
function explodeOperators(array $strings) : array
{
$operators = ['>=','<=','!=','<>','<','>','=']; // do not change this order
$result = [];
foreach ($strings as $expression) {
$found = false;
foreach ($operators as $operator) {
$exploded = explode($operator, $expression);
if (count($exploded) > 1 and $found === false) {
$found = true;
array_splice($exploded, 1, 0, $operator);
$result[] = $exploded;
}
}
}
return $result;
}
$result = explodeOperators($strings);
print_r($result);
That will result:
Array
(
[0] => Array
(
[0] => SalesOrderNumber
[1] => =
[2] => SOH123
)
[1] => Array
(
[0] => CustomerID
[1] => =
[2] => 1
)
[2] => Array
(
[0] => BigOrEqual
[1] => >=
[2] => 44
)
[3] => Array
(
[0] => SmallOrEqual
[1] => <=
[2] => 67
)
[4] => Array
(
[0] => NotEqual
[1] => !=
[2] => 123
)
[5] => Array
(
[0] => NotEqual
[1] => <>
[2] => 2000
)
[6] => Array
(
[0] => Smaller
[1] => <
[2] => 21
)
[7] => Array
(
[0] => Bigger
[1] => >
[2] => 12
)
)
Related
It seems that there are dozens of topic with similar problem, and mostly all have the same answer: use array_filter, array_map. The problem is that I used them, but it didn't quite help. So I have an array (is built from data in csv file):
Array
(
[0] => Array
(
[0] => name
[1] => title
[2] => email
)
[1] => Array
(
[0] => First
[1] => title 1
[2] => email1#ex.com
)
[2] => Array
(
[0] => second
[1] => title 1
[2] => email2#ex.com
)
[3] => Array
(
[0] =>
)
[4] => Array
(
[0] => third
[1] => title 1
[2] => email3#ex.com
)
[5] => Array
(
[0] =>
)
[6] => Array
(
[0] =>
[1] =>
[2] =>
)
)
I have to delete all empty arrays. So I use such code:
while (($row = fgetcsv($file, 8192, ';')) !== false) {
if (array(null) !== $row) { // ignore blank lines
$csv[] = $row;
}
}
$array = array_filter(array_map('array_filter', $csv));
$array now is:
Array
(
[0] => Array
(
[0] => name
[1] => title
[2] => email
)
[1] => Array
(
[0] => First
[1] => title 1
[2] => email1#ex.com
)
[2] => Array
(
[0] => second
[1] => title 1
[2] => email2#ex.com
)
[3] => Array
(
[0] => third
[1] => title 1
[2] => email3#ex.com
)
[4] => Array
(
[1] =>
)
)
Why there is a 4th array with empty value? I need to get rid of it.
gettype($array[4][1]) = string
UPDATE
The csv has empty rows, and even just a ";" delimiter without any string. I cannot influence on the process of inserting data into this csv.
The csv looks like this:
1 row: name;title;email
2 row: First;title 1;email1#ex.com
3 row: second;title 1;email2#ex.com
4 row:
5 row: third;title 1;email3#ex.com
6 row:
7 row: ; ;
and mostly all have the same answer: use array_filter, array_map.
array_filter is a good approach, but I wouldn't use array_map, but array_reduce:
$array = array_filter(
$csv,
function ($value) {
return 0 < array_reduce(
$value,
function ($carry, $item) {
return empty(trim($item)) ? $carry : $carry + 1;
},
0
);
}
);
With array_reduce, I count the non-empty elements in an array. And if there are zero non-empty elements, the array is thrown away with array_filter.
For reference, the PHP 7.4-syntax, which looks nicer in my eyes, but could be a bit confusing at first
$array = array_filter(
$csv,
fn ($val) => 0 < array_reduce(
$val,
fn ($carry, $item) => empty(trim($item)) ? $carry : $carry + 1,
0
)
);
I have a multidimensional array whose depth is uncertain or let's say depends on how many categories & sub-categories(& sub-sub-categories) are there in magento website, as I calling function $product->getCategoryIds().
Now let's assume I get all product's(that are added in the cart/checkout) category tree-hierarchy as multidimensional array like below:
Array
(
[0] => Array
(
[0] => 40
)
[1] => Array
(
[0] => 40
)
[2] => Array
(
)
[3] => Array
(
)
[4] => Array
(
[0] => 16
)
[5] => Array
(
[0] => 16
)
)
But as it is an ecomerce site's category heirarchy, you can safely imagine, site could have any depth of categories, sub-categories, sub-sub-categories and so on. Unknown hierarchy of product categories so unknown depth of category ids array.
Now comes my confusion and query, how to check if every sub-array and/or sub-sub-array contains a particular category Id(eg. 40 or 16) or not, by using a combination of various php array functions and using as less loops as possible ?
All I can think of is nested for/foreach loops which has performance overheads, so I am looking for better alternatives to that.
It sounds like you are looking for something like this:
$ids = $product->getCategoryIds();
$non_empty_ids = array();
foreach ($ids as $key => $value) {
if (!count($value)) {
// The value has no offsets, lets skip this one
continue;
}
$non_empty_ids[] = $value;
}
I prefer using recursion when you are not certain about the input(the structure of the array)
you can use something like this
function searchCategoriesForValue(array $array, array $needles)
{
$matchsCount = 0;
foreach ($array as $element){
if(is_array($element)){
$matchsCount += searchCategoriesForValue($element, $needles);
}else{
if(in_array($element, $needles)){
$matchsCount ++;
}
}
}
return $matchsCount;
}
Example usage
<?php
// nested array that has the number 16 six times.
$array = [ [0,1,2,3,[16,40,[0,1]]],
[0,1,2,3,[16,40,[0,1]]],
[0,1,2,3,[16,40,[0,1]]],
[0,1,2,3,[16,40,[0,1]]],
[0,1,2,3,[16,40,[0,1]]],
[],
[0,1,2,3,[16,40,[0,1]]]];
$count = searchCategoriesForValue($array,[16]);
// 16 has been found 6 times
var_dump($count);
$count = searchCategoriesForValue($array,[16,40]);
// 16 and 40 have been found 12 times
exit;
this outputs
int(6)
live demo (https://eval.in/835973);
You can use array_filter() to remove empty categories from the array
<?php
$myarray = array('0' =>
array('0' => '40'),
'1' => array('0' => '40'),
'2' => array(),
'3' => array(),
'4' => array('0' => '16'),
'5' => array('0' => '16'),
'6' => array('0' =>
array('0' => '40'),
'1' => array('0' => '40'),
'2' => array(),
'3' => array(),
'4' => array('0' => '16'),
'5' => array('0' => '16')
)
);
$b = array_map("array_filter", $myarray);
echo "<pre>";
print_r(array_filter($b));
echo "</pre>";
?>
Result:
Array
(
[0] => Array
(
[0] => 40
)
[1] => Array
(
[0] => 40
)
[4] => Array
(
[0] => 16
)
[5] => Array
(
[0] => 16
)
[6] => Array
(
[0] => Array
(
[0] => 40
)
[1] => Array
(
[0] => 40
)
[4] => Array
(
[0] => 16
)
[5] => Array
(
[0] => 16
)
)
)
I have this kind of sting i variable in php:
$hccRol = GA#COR,OP#COR,FOR#TB,GA#LTS,FOR#MOD,GA#PMAI,LDR#LID,HCC#LAD,HN#LAD,OP#LAD,GA#LAD,GA#WM,OP#WM,HN#WM,OP#VZ,HN#VZ,GA#VZAI
I want convert this to multi array that look somthing like this:
Array ( [GA] => Array ( [1] => COR
[2] => LTS
[3] => LAD
[4] => WM
[5] => VZAI
)
[FOR] => Array( [1] => TB
[2] => MOD
)
[OP] => Array( [1] => COR
[2] => WM
[3] => VZ
)
)
So the # determines in witch primary array the value must come
CBroe gave you the steps to do it the traditional way, so just for fun because I was bored:
parse_str(str_replace(array('#',','), array('[]=','&'), $hccRol ), $result);
print_r($result);
PHP >= 5.4.0:
parse_str(str_replace(['#',','], ['[]=','&'], $hccRol ), $result);
The following is a basic way to explode on the delimiting characters, iterate the data, and declare the new array structure. I will honor your desired output and start the keys at 1 instead of the standard 0.
Code: (Demo)
$hccRol = "GA#COR,OP#COR,FOR#TB,GA#LTS,FOR#MOD,GA#PMAI,LDR#LID,HCC#LAD,HN#LAD,OP#LAD,GA#LAD,GA#WM,OP#WM,HN#WM,OP#VZ,HN#VZ,GA#VZAI";
foreach (explode(",", $hccRol) as $item) {
$halves = explode("#", $item, 2);
if (!isset($result[$halves[0]])) {
$result[$halves[0]][] = null; // add zero key placeholder element
}
$result[$halves[0]][] = $halves[1];
unset($result[$halves[0]][0]); // remove the placeholder element
}
var_export($result);
Output:
array (
'GA' =>
array (
1 => 'COR',
2 => 'LTS',
3 => 'PMAI',
4 => 'LAD',
5 => 'WM',
6 => 'VZAI',
),
'OP' =>
array (
1 => 'COR',
2 => 'LAD',
3 => 'WM',
4 => 'VZ',
),
'FOR' =>
array (
1 => 'TB',
2 => 'MOD',
),
'LDR' =>
array (
1 => 'LID',
),
'HCC' =>
array (
1 => 'LAD',
),
'HN' =>
array (
1 => 'LAD',
2 => 'WM',
3 => 'VZ',
),
)
If you are happy to have the 0 indexes, then just remove the if{} block and the unset() line.
Racking my brain on this, found many examples of similar situations however the solutions don't seem to match up.
I have two arrays being built as the result of SQL queries on different databases.
One is coming through formatted as such:
$data = array([$sku] => array(['LocalSKU'] => $sku,
['Price'] => $msrp,
['Price2'] => $wholesale,
['Price3'] => $distributor,
['Price4'] => $map))
The other array is formatted as such:
$matchme = array([0] => array(['entity_id'] => $entity_id,
['sku'] => $sku,
['type_id'] => $type_id))
Currently, I can get the individual data to match up via:
echo $matchme[0]['sku'];
echo $matchme[0]['entity_id'];
echo $matchme[0]['type_id'];
echo $data[$matchme[0]['sku']]['Price'];
echo $data[$matchme[0]['sku']]['Price2'];
echo $data[$matchme[0]['sku']]['Price3'];
echo $data[$matchme[0]['sku']]['Price4'];
However, when I try and merge the matching rows in both arrays, I get an empty array. The $data array contains 74 unique $sku, and $matchme is the result of checking those $sku's against a database and returning an array with 61 elements. So, the combined array should have 61 elements with matched pricing data based on the $sku.
How I am attempting to build the combined array is below, can anyone point me towards what I am doing wrong?
foreach($matchme as $key){
if(in_array($matchme[$key]['sku'], $data)){
$matched_luggage[$matchme[$key]['sku']][] = array(
'sku' => $matchme[$key]['sku'],
'entity_id' => $matchme[$key]['entity_id'],
'type_id' => $matchme[$key]['type_id'],
'MSRP' => $data[$matchme[$key]['sku']]['Price'],
'Wholesale' => $data[$matchme[$key]['sku']]['Price2'],
'Distributor' => $data[$matchme[$key]['sku']]['Price3'],
'MAP' => $data[$matchme[$key]['sku']]['Price4']
);
}
}
In the above example, evaluate $key as 0, and the value of ['sku'] are matching.
------------------------Edited-------------------------
Per request, here is the result of print_r($data) truncated for space:
Array
(
[12PK-TITANIUM-CR123A] => Array
(
[LocalSKU] => 12PK-TITANIUM-CR123A
[Price] => 11.76
[Price2] => 10.32
[Price3] => 0
[Price4] => 0
)
[AA-CLAMSHELL] => Array
(
[LocalSKU] => AA-CLAMSHELL
[Price] => 0.25
[Price2] => 0
[Price3] => 0
[Price4] => 0
)
[AAA-CLAMSHELL] => Array
(
[LocalSKU] => AAA-CLAMSHELL
[Price] => 0.25
[Price2] => 0
[Price3] => 0
[Price4] => 0
)
[AE-AEL280PI] => Array
(
[LocalSKU] => AE-AEL280PI
[Price] => 0
[Price2] => 0
[Price3] => 0
[Price4] => 0
) )
Per request, here is the result of print_r($matchme) truncated for space:
Array
(
[0] => Array
(
[entity_id] => 693
[sku] => 12PK-TITANIUM-CR123A
[type_id] => simple
)
[1] => Array
(
[entity_id] => 2596
[sku] => AE-AEL480HL
[type_id] => simple
)
[2] => Array
(
[entity_id] => 2597
[sku] => AE-AEL600-T6
[type_id] => simple
)
[3] => Array
(
[entity_id] => 2598
[sku] => AE-AEWL2
[type_id] => simple
) )
Per request, here is the desired result of $matched_luggage:
$matched_luggage = array( [12PK-TITANIUM-CR123A] => array([sku] => 12PK-TITANIUM-CR123A,
[entity_id] => 693,
[type_id] => simple,
[Price] => 11.76,
[Price2] => 10.32,
[Price3] => 0,
[Price4] => 0))
with an additional array per matched sku.
Try this:
foreach ($matchme as $arrProduct) {
if (isset($data[$arrProduct['sku']])) {
$arrMerged[$arrProduct['sku']]=array_merge($arrProduct, $data[$arrProduct['sku']]);
}
}
print_r($arrMerged);
The reason your code doesn't work is here:
if(in_array($matchme[$key]['sku'], $data)) [...]
What in_array() does is tell you whether your needle (in your case the SKU string) exists as a value of array haystack (in your case, $data). You are essentially trying to match a string to an array, rather than another string.
What you really want is just to match the SKU string to the key of $data, for which isset() is probably the simplest approach.
Assuming your first array as $first and second array as $second
foreach ($first as $key => $each) {
foreach ($second as $secondeach) {
if($secondeach['sku'] == $key) {
$first[$key] = array_merge($first[$key], $secondeach);
// unset since you do not want LocalSKU value anymore.
unset($first[$key]['LocalSKU']);
}
}
}
$first is your array you wanted.
i have wrote a script to produce an array of data but now want to display in order of score. The array outputs as follows;
[display_name] => Array
(
[0] => ACT_Web_Designs
[1] => user1_design
[2] => user2_design
)
[proffesion] => Array
(
[0] => Web Developer
[1] => web developer
[2] => Web Developer
)
[score] => Array
(
[0] => 15
[1] => 6
[2] => 15
)
[img] => Array
(
[0] => ./?0000=gif&0001=3fadb8c362ff39f3322909899ff14760&0002=prof_pic
[1] =>
[2] =>
)
so in a nutshell I am wanting it to be converted as follows;
[display_name] => Array
(
[0] => ACT_Web_Designs
[1] => user2_design
[2] => user1_design
)
[proffesion] => Array
(
[0] => Web Developer
[1] => web developer
[2] => Web Developer
)
[score] => Array
(
[0] => 15
[1] => 15
[2] => 6
)
[img] => Array
(
[0] => ./?0000=gif&0001=3fadb8c362ff39f3322909899ff14760&0002=prof_pic
[1] =>
[2] =>
)
I have been looking at asort() but cant get anything to work. any help would be much appreciated.
This is exactly where the PHP's array_multisort comes to use. It is a case where you want to sort many arrays based on the comparison happening in just one of them.
I've modified the array score to have distinct values.
<?php
$arr = array(
'display_name' => array('ACT_Web_Designs','user1_design','user2_design'),
'proffesion' => array('Web Developer','web developer','web developer'),
'score' => array(12,6,15),
'img' => array('./?0000=gif&0001=3fadb8c362ff39f3322909899ff14760&0002=prof_pic','','')
);
var_dump($arr);
array_multisort($arr['score'], SORT_ASC, SORT_NUMERIC,
$arr['display_name'],
$arr['proffesion'],
$arr['img']
);
var_dump($arr);
?>
Here goes a working demo.
How about this simpler one
$arr = array("k"=>array("A","B","C"),"l"=>array(15,6,15),"n"=>array("k","l","n"));
array_multisort($arr["k"],SORT_NUMERIC,SORT_DESC,$arr["l"],$arr["n"]);
var_dump($arr);
Doesn't it work to just rsort the score array?
rsort($data['score'], SORT_NUMERIC);
I have managed to do this, i was just after a more efficient way;
$array = array(
'display_name' => array(0 => 'ACT_Web_Designs', 1 => 'user1_design', 2 => 'user2_design' ),
'proffesion' => array( 0 => 'Web Developer', 1 => 'web developer', 2 => 'Web Developer' ),
'score' => array( 0 => 15, 1 => 6, 2 => 15 ),
'img' => array( 0 => './?0000=gif&0001=3fadb8c362ff39f3322909899ff14760&0002=prof_pic', 1 => '', 2 => '' )
);
arsort($array['score'], SORT_NUMERIC );
foreach($array['score'] as $key => $val ) {
$newarray['display_name'][] = $array['display_name'][$key];
$newarray['proffesion'][] = $array['proffesion'][$key];
$newarray['score'][] = $array['score'][$key];
$newarray['img'][] = $array['img'][$key];
}
print_r($newarray);
returns
Array
(
[display_name] => Array
(
[0] => ACT_Web_Designs
[1] => user2_design
[2] => user1_design
)
[proffesion] => Array
(
[0] => Web Developer
[1] => Web Developer
[2] => web developer
)
[score] => Array
(
[0] => 15
[1] => 15
[2] => 6
)
[img] => Array
(
[0] => ./?0000=gif&0001=3fadb8c362ff39f3322909899ff14760&0002=prof_pic
[1] =>
[2] =>
)
)
Use rsort()
<?php
$fruits = array("lemon", "orange", "banana", "apple");
rsort($fruits);
reset($fruits);
while (list($key, $val) = each($fruits)) {
echo "$key = $val\n";
}
?>
This example would display:
0 = orange
1 = lemon
2 = banana
3 = apple
The most elegant solution that I could find would not reorder the data structure but merely access it in a different fashion.
$scores = $data['score'];
arsort($scores);
$keys_ordered_by_score = array_keys($scores);
Now you can, say, grab the display_name and "proffesion" that has the highest score by the following:
$first_place = $keys_ordered_by_score[0];
echo $data['display_name'][$first_place],
' is a ', $data['proffesion'][$first_place];
Of course, if you really need to reorder the data structure, this idea is useless to you. Any of the other answers using array_multisort() will likely suit that need.
This will not re-sort them for you, but it will let you go through them in the order you want. You can reassign them if you want, but I'd just use this for the output order.
Edit: This will not work, due to the possibility of non-unique key values. See Comments below, and learn from my mistake
$sort_order = $array['score'];
arsort($sort_order);
$sort_order = array_flip($sort_order);
foreach($sort_order as $key){
echo $array['display_name'][$key].' - '.$array['score'][$key];
}