PHP Regex removing unwanted values from pattern - php

I have a large array of scraped names and prices similar to the following:
Array([0] => apple3 [1] => £0.40 [2] => banana6 [3] => £1.80 [4] => lemon [5] => grape [6] => pear5 [7] => melon4 [8] => £2.32 [9] => kiwi [10] => £0.50)
I would like to remove the fruit names that are not immediately followed by a price. In the above example this would remove: [4] => lemon [5] => grape [6] => pear5 resulting in the following output:
Array([0] => apple3 [1] => £0.40 [2] => banana6 [3] => £1.80 [7] => melon4 [8] => £2.32 [9] => kiwi [10] => £0.50)
If the array needs to be converted to a string in order for me to do this that is not a problem, nor is adding values between the array items in order to aid with regex searches. I have so far been unable to find the correct regular expression to do this using preg_match and preg_replace.
The most important factor is the need to maintain the sequential order of the fruits and prices in order for me at a later stage to convert this into an associative array of fruits and prices.
Thanks in advance.

Why involve regular expressions? This is doable with a simple foreach loop wherein you iterate over the array and remove names that follow names:
$lastWasPrice = true; // was the last item a price?
foreach ($array as $k => $v) {
if (ctype_alpha($v)) {
// it's a name
if (!$lastWasPrice) {
unset($array[$k]); // name follows name; remove the second
}
$lastWasPrice = false;
}
else {
// it's a price
$lastWasPrice = true;
}
}

The following code does both of your tasks at once: getting rid of the fruit without value and turning the result into an associative array of fruits with prices.
$arr = array('apple', '£0.40', 'banana', '£1.80', 'lemon', 'grape', 'pear', 'melon', '£2.32', 'kiwi', '£0.50' );
preg_match_all( '/#?([^£][^#]+)#(£\d+\.\d{2})#?/', implode( '#', $arr ), $pairs );
$final = array_combine( $pairs[1], $pairs[2] );
print_r( $final );
First, the array is converted to a string, separated by '#'. The regex captures all groups of fruits with prices - each stored as a separate subgroup in the result. Combining them into an associative array is a single function call.

Something like this might help you
$array = ...;
$index = 0;
while (isset($array[$index + 1])) {
if (!is_fruit($array[$index + 1])) {
// Not followed by a fruit, continue to next pair
$index += 2;
} else {
unset($array[$index]); // Will maintain indices in array
$index += 1;
}
}
Not tested though. Also, you need to create the function is_fruit yourself ;)

Without reformatting it, I don't think you can do it with preg_match or preg_replace-- maybe, but nothing is coming to mind.
What is creating that array? If possible, I would alter it to look more like:
Array([apple] => £0.40 [banana] => £1.80 [lemon] => [grape] => '' [pear ] => '' [melon => £2.32 [kiwi] => £0.50)
Then array_filter($array) is all you'd need to clean it up. If you can't alter the way the original array is created I'd lean towards creating key/value array out of the original.

Try replacing the pattern ** => ([a-zA-Z])** with ** => £0.00 $1**
Basically searching for the context where there is null price and inserting zero pounds.
Hope this helps.
Good luck

Simply do this :
<?php
for($i=0;$i<count($my_array);$i++)
{
if($my_array[$i+1]value=="")
unset($my_array[$i])
}
?>

assume $a is your array.
function isPrice($str) {
return (substr($str, 0, 1) == '£');
}
$newA = array();
for($i=0;$i<count($a);$i++) {
if( isPrice($a[$i]) != isPrice($a[$i+1]) ){
$newA[] = $a[$i];
}
}

Related

How can I combine multiple values into a single value in an array?

I have this array:
$dataArr = Array(
[0] => Repper
[1] => Pavel
[2] => 7.1.1970
[3] => K.H.Máchy //start of address
[4] => 1203/2,
[5] => Bruntál // end of address
[6] => EM092884
[7] => 7.1.2019
);
I need to modify this array so that the address (index 3 to index 6) is below index 3, but indexes 4 and 5 will be removed. Thus, the newly modified array will have indexes from 0 to 5 (6 values). The number of values ​​from index 3 (from the beginning of the address) may be even greater and the address may end, for example, with index number 9. But the beginning of the address is always from index 3.
Expected result:
$dataArr= Array(
[0] => Repper
[1] => Pavel
[2] => 7.1.1970
[3] => K.H.Máchy 1203/2, Bruntál
[4] => EM092884
[5] => 7.1.2019
);
My idea was as follows. I try something like this:
I go through the matrix from index 3 and look for a regular match (the value just after the end of the address). Until the array value matches the regex, I compile the values ​​into string.
$address = NULL; //empty variable for address from $dataArr
foreach($dataArr as $k => $val) {
if($k >= 3) {
if(! preg_match('/^\[A-Za-z]{2}\d{6}/', $val)) {
$address .= $val;
//Then put this variable $address in position $dataArr[3]
}
}
}
But it seems that the 'if' condition with preg_match is still true. I need the foreach cycle to stop before index 6, but the cycle is still working, to last value of array. Where's the mistake? This problem hinders me in completing the script. Thank you very much.
One other possibility, pop off the beginning and end
$first = array_splice($dataArr, 0, 3);
$last = array_splice($dataArr, -2);
Then implode the remaining part and put it all back together.
$dataArr = array_merge($first, [implode(' ', $dataArr)], $last);
// or in PHP 7.4
$dataArr = [...$first, implode(' ', $dataArr), ...$last];
This should work regardless of the size of the address, but it does totally depend on the last two elements after the address always being present, so if there's any way those would be missing sometimes you'll need something a little more complicated to account for that.
Why overcomplicate things with regexp and loops? Just literally do what you describe: if your address runs from n to m, take the array slice from n to m, implode that to a string, set array[n] to that string, and then remove fields [n+1...m] from your array:
function collapse_address($arr, $start, $end) {
$len = $end - $start;
// collapse the address:
$arr[$start] = join(" ", array_slice($arr, $start, $len));
// remove the now-duplicate fields:
array_splice($arr, $start + 1, $len - 1);
// and we're done.
return $arr;
}
$arr = Array(
'Repper',
'Pavel',
'7.1.1970',
'K.H.Máchy', //start of address
'1203/2',
'Bruntál', // end of address
'EM092884',
'7.1.2019'
);
$arr = collapse_address($arr, 3, 6);
result:
Array
(
[0] => Repper
[1] => Pavel
[2] => 7.1.1970
[3] => K.H.Máchy 1203/2 Bruntál
[4] => EM092884
[5] => 7.1.2019
)
Of course, you might not want $end to be exclusive, but that's up to you.

Create PHP Variables dynamically in foreach loop

I am just trying to create PHP variables dynamically. below is the code I have tried.
if($BrickTerritorys)
{
foreach($BrickTerritorys as $index=>$BrickTerritory)
{
${"$T.$index"}= $BrickTerritory->TerritoryID;
${"'Weightage'.$index"} = $BrickTerritory->Weightage;
}
echo $T1."-".$T2."--".$Weightage1."---".$Weightage2; exit;
}
while
$BrickTerritorys is
[1] => stdClass Object
(
[id] => 119
[TerritoryID] => HYD-2-CMD
[BrickCode] => 16
[BrickName] => BUHURO
[Weightage] => 40.00
[BPCode] => bp00066
[GroupCode] => CMD
)
[2] => stdClass Object
(
[id] => 36330
[TerritoryID] => HYD-1-CMD
[BrickCode] => 16
[BrickName] => BUHURO
[Weightage] => 60.00
[BPCode] => bp00066
[GroupCode] => CMD
)
When I print in the last, nothing gets printed. Any help is much appreciated, please.
Thanks in advance
${"T$index"} as well as ${"Weightage$index"}
you don't need the dot,or you can use ${'T' . $index}. look at the dot. it's not addition operation while it in "". following this code:
if($BrickTerritorys)
{
foreach($BrickTerritorys as $index=>$BrickTerritory)
{
${"$T.$index"}= $BrickTerritory->TerritoryID;
${"'Weightage'.$index"} = $BrickTerritory->Weightage;
}
echo $T1."-".$T2."--".$Weightage1."---".$Weightage2; exit;
}
Try changing those lines like this:
${"T$index"}= $BrickTerritory->TerritoryID;
${"Weightage$index"} = $BrickTerritory->Weightage;
In your code ${"$T.$index"} $T is searching for variable, and you should get undefined variable $T, so you have to remove $ sign, if you want to have T1, T2 variables.
After that, ${"'Weightage'.$index"}, the apostrophes between Weightage means your variable will look like 'Weightage'.1, 'Weightage'.2.. and etc.
This can be done a few different ways without variable variables AND produce a completely dynamic outcome.
Here's one: (Demo)
$array = (array)$BrickTerritorys; // cast as array
$tids = array_column($array, 'TerritoryID'); // isolate column data
$was = array_column($array, 'Weightage'); // isolate column data
$merged = array_merge($tids, $was); // add 2nd array data after 1st array data
foreach ($merged as $i => $v) {
echo str_repeat('-', $i) , $v; // increase hyphens on each iteration starting from 0
}
Output: (notice, no hardcoded echo)
HYD-2-CMD-HYD-1-CMD--40.00---60.00

Reordering an array, moving all strings that contain an underscore to the bottom

I am trying to move the strings that contain an underscore _ to the bottom of my array. I'm assuming usort() is the best way but not sure how to do this in the most efficient way. Let's say my array is '1','34_1','35_1','36_1','7','41_4','38_5','5','41_5','44_5','45_5'
usort(['1','34_1','35_1','36_1','7','41_4','38_5','5','41_5','44_5','45_5'], function (){...});
UPDATE: I think I found a way to do it:
$myarray = ['1','34_1','35_1','36_1','7','41_4','38_5','5','41_5','44_5','45_5'];
function sortem($myarray) {
foreach ($myarray as $index=>$item)
{
if (preg_match('/^_+$/', $item))
{
unset($myarray[$index]);
$myarray[$index] = $item;
}
}
}
usort($myarray, "sortem");
$tagsuri = array_reverse($tagsuri);
Is there a better way?
I am sure you could find a way to usort() but a quick way to do this would just be to split the array into two, then combine them back:
<?php
$myarray = array('1','34_1','35_1','36_1','7','41_4','38_5','5','41_5','44_5','45_5');
foreach($myarray as $value) {
# Put all numbers into one array, underscored into a second
if(strpos($value,'_') !== false)
$strArr[] = $value;
else
$numArr[] = $value;
}
# Sort both arrays
# You'll probably want to do checks to see that they are not empty first
asort($strArr);
asort($numArr);
# Combine arrays
print_r(array_merge($numArr,$strArr));
Gives you:
Array
(
[0] => 1
[1] => 5
[2] => 7
[3] => 34_1
[4] => 35_1
[5] => 36_1
[6] => 38_5
[7] => 41_4
[8] => 41_5
[9] => 44_5
[10] => 45_5
)

PHP - Sort Indexed Array by even elements

I'm trying to sort an array in PHP. The array is names in the even indices and times (MM:SS.XX).
Array ( [0] => Emma Hogan [1] => 09:12.55 [2] => Bob Harrison [3] => 12:00.15 [4] => Dave Haenze [5] => 10:00.98 [6] => Tau Newman [7] => 07:05.15 [8] => Cai Jones [9] => 44:15.59 )
What I want it to do is grab the every other element (the times) and sort the array by shortest time to longest. My code for it looks like this:
for($i=1;$i<$arrlength;$i+=2) {
$j = $i;
while (($j>0)&&($array[($j-2)] > $array[$j])){
$temp = $array[$j];
$array[$j] = $array[($j-2)];
$array[($j-2)] = $temp;
$temp2 = $array[($j+1)];
$array[($j+1)] = $array[($j-1)];
$array[($j-1)] = $temp;
$j = $j - 2;
}
}
However, the output it returns looks like this:
Emma Hogan
07:05.15
07:05.15
09:12.55
Bob Harrison
10:00.98
10:00.98
12:00.15
Dave Haenze
44:15.59
What am I doing wrong? What do I need to change to make it sort properly?
You've indicated that you can change the array format. If so consider the following:
$array = array ( 'Emma Hogan' => '09:12.55' );
asort($array); //or arsort()
Or:
$array = array ( '09:12.55' => 'Emma Hogan' );
ksort($array); //or krsort()
As you can see your array layout makes little sense for what you want to accomplish. I sugest fixing that first, instead of bending the head and trying to work on bad data structure. I.e.:
$tmp = array();
for($i=0; $i<count($srcArray); $i+=2) {
$tmp[$srcArray[i]] = $srcArray[$i+1];
}
and then you can sort $tmp using usort(). Or even better, you can convert your times to numeric value and then just user ordinary sort() to do the job.

How to remove ALL duplicate values from an array in PHP?

I want to check only the value [id] for duplicates, and remove all keys where this "field" [id] is a duplicate.
Example: If I have numbers 1,2,1. I want the result to be 2, not 1,2. And criteria for duplicates is determined only by checking [id], not any other "field".
Original array:
Array
(
[0] => Array
(
[name] => John
[id] => 123
[color] => red
)
[1] => Array
(
[name] => Paul
[id] => 958
[color] => red
)
[2] => Array
(
[name] => Jennifer
[id] => 123
[color] => yellow
)
)
The result I want:
Array
(
[0] => Array
(
[name] => Paul
[id] => 958
[color] => red
)
)
I agree with everyone above, you should give us more information about what you've tried, but I like to code golf, so here's a completely unreadble solution:
$new_array = array_filter($array, function($item) use (&$array){
return count(array_filter($array, function($node) use (&$item){
return $node['id'] == $item['id'];
})) < 2;
});
This should be fairly easy to accomplish with a couple of simple loops:
set_time_limit(0); // Disable time limit to allow enough time to process a large dataset
// $items contains your data
$id_counts = array();
foreach ($items as $item) {
if (array_key_exists($item['id'], $id_counts)) {
$id_counts[$item['id']]++;
} else {
$id_counts[$item['id']] = 1;
}
}
for ($i = count($items); $i >= 0; $i--) {
if ($id_counts[$items[$i]['id']] > 1) {
array_splice($items, $i, 1);
}
}
Result:
Array
(
[0] => Array
(
[name] => Paul
[id] => 958
[color] => red
)
)
While there are neater ways to do it, one advantage of this method is you're only creating new arrays for the list of ids and duplicate ids and the array_splice is removing the duplicates from the original array, so memory usage is kept to a minimum.
Edit: Fixed a bug that meant it sometimes left one behind
This is a very basic approach to the answer and I am sure there are much better answers however I would probably start by doing it the way I would on paper.
I look at the first index, check its value. Then I go through every other index making note of their index if the value is the same as my originally noted value. Once I have gone through the list if I have more than one index with that particular value I remove them all (starting with the highest index, so as to not affect indexes of the others while deleting).
Do this for all other indexes till you reach the end of the list.
It is long winded but will make sure it removes all values which have duplicates. and leaves only those which originally had no duplicates.
function PickUniques(array $items){
// Quick way out
if(empty($items)) return array();
$counters = array();
// Count occurences
foreach($items as $item){
$item['id'] = intval($item['id']);
if(!isset($counters[$item['id']])){
$counters[$item['id']] = 0;
}
$counters[$item['id']]++;
}
// Pop multiples occurence ones
foreach($counters as $id => $occurences){
if($occurences > 1){
unset($counters[$id]);
}
}
// Keep only those that occur once (in $counters)
$valids = array();
foreach($items as $item){
if(!isset($items[$item['id']])) continue;
$valids[$item['id']] = $item;
}
return $valids;
}
Try this one :)

Categories