php how to split an array string into two by condition - php

i have a simple array of prices, which is formed as a result of data entry by users
array(4) {
[0] => string(4) "18"
[1] => string(4) "20"
[2] => string(4) "10"
[3] => string(4) "17"
}
The minimum and maximum values ​​are displayed in the frontend like 10-20$
But, in one field, the user can enter a range of values, like this:
array(3) {
[0]=> string(9) "18-28"
[2]=> string(9) "10-15"
[3]=> string(9) "16-22"
}
And then the range is displayed as 10-15-18-28$
How to break the lines of array into two, if the line has a "-" or "/"?
Thanks for help:)

You might use explode and use the dash or forward slash as the delimiter. If you want to display the highest and the lowest, you could sort them and use for example rsort.
$a = [
"18-28",
"18-29",
"10-15",
"16-22"
];
rsort($a);
print_r($a);
Result
Array
(
[0] => 18-29
[1] => 18-28
[2] => 16-22
[3] => 10-15
)
Then you can get the first item from the array as the highest and the last item as the lowest range.
echo reset($a).'$'; // 18-29$
echo end($a).'$'; // 10-15$
Php demo

You could use a combination of explode and implode:
$a = [
'18-28',
'10-15',
'16-22'
];
$b = explode('-',implode('-',$a));
echo min($b).'-'.max($b).'$'; // result: 10-28$
If the input may also contain spaces or other dummy characters, or people erratically enter price ranges as "18/28" or "18~28" instead of "10-28" or whatever, a more fool-proof way would be:
$a = [
// crappy formatted example input
'18 - 28 ',
'10/15$',
' 16~22'
];
$b = explode(' ',trim(preg_replace('#[^0-9.]+#si',' ',implode(' ',$a))));
echo min($b).'-'.max($b).'$'; // result: 10-28$
What this does is:
implode all elements of $a into a single string, glued together with spaces
replace all non-numeric parts of the string (anything that isn't a digit or dot) into a single space
trim the string to remove any redundant spaces at the beginning and end (to avoid empty elements)
explode the string using a space as separator, to create a new array containing all individual numbers
take the min and max of the resulting array
If you don't want to support floating point numbers (like 9.95) then remove the dot in the regular expression, i.e. that becomes '#[^0-9]+#si'.

Related

Return the indices of the largest + second largest values in a PHP array?

Goal: given a string containing a list of comma-separated float values, return the indices of the largest and second largest floats
"Largest" and "Second largest" should be sorted numerically (not as strings); e.g., "100" is larger than "2"
Example:
given a string: "11,2,1,100,1.5"
return the indices: 3 and 0 (corresponding to numeric values 100 and 11)
What I've tried:
New to PHP, but I feel like I'm so close with the following code, but can't figure out how to get the actual index values:
$x_string = "11,2,1,100,1.5";
$x_array = explode(",", $x_string);
// apply floatval to every element of array
function float_alter(&$item) { $item = floatval($item); }
array_walk($x_array, float_alter);
$temp = $x_array;
arsort($temp);
var_dump($temp);
Outputs:
array(5) { [3]=> float(100) [0]=> float(11) [1]=> float(2) [4]=> float(1.5) [2]=> float(1) }
You can split and floatval in one step. Then sort descending to have largest first. The keys are still kept.
$string = "11,2,1,100,1.5";
$floats = array_map('floatval', explode(',', $string));
arsort($floats, SORT_NUMERIC | SORT_DESC);
print_r($floats);
list($largestKey, $secondLargestKey) = array_keys($floats);
echo "$largestKey, $secondLargestKey";
prints
Array
(
[3] => 100
[0] => 11
[1] => 2
[4] => 1.5
[2] => 1
)
3, 0
There is no benefit in calling floatval() on every value after exploding because you are only interested in the keys anyhow.
Just sort your array in DESCending order while evaluating the values as numbers and preserving the original keys. Here is a simpler approach:
Explode
Sort
Isolate the top two elements
Return all keys
Code: (Demo)
$array = explode(',', '11,2,1,100,1.5');
arsort($array, SORT_NUMERIC);
var_export(array_keys(array_slice($array, 0, 2, true)));
Markus's answer could be modified to be this and return the same data, but it generates an unnecessarily long array of keys before truncating (as a means to avoid a function call). (Demo)
$array = explode(',', '11,2,1,100,1.5');
arsort($array, SORT_NUMERIC);
[$first, $second] = array_keys($array);
var_export([$first, $second]);

PHP how to avoid mixed letter-number when extracting chunks of numbers from string

I'm writing a PHP function to extract numeric ids from a string like:
$test = '123_123_Foo'
At first I took two different approaches, one with preg_match_all():
$test2 = '123_1256_Foo';
preg_match_all('/[0-9]{1,}/', $test2, $matches);
print_r($matches[0]); // Result: 'Array ( [0] => 123 [1] => 1256 )'
and other with preg_replace() and explode():
$test = preg_replace('/[^0-9_]/', '', $test);
$output = array_filter(explode('_', $test));
print_r($output); // Results: 'Array ( [0] => 123 [1] => 1256 )'
Any of them works well as long as the string does not content mixed letters and numbers like:
$test2 = '123_123_234_Foo2'
The evident result is Array ( [0] => 123 [1] => 1256 [2] => 2 )
So I wrote another regex to get rid off of mixed strings:
$test2 = preg_replace('/([a-zA-Z]{1,}[0-9]{1,}[a-zA-Z]{1,})|([0-9]{1,}[a-zA-Z]{1,}[0-9]{1,})|([a-zA-Z]{1,}[0-9]{1,})|([0-9]{1,}[a-zA-Z]{1,})|[^0-9_]/', '', $test2);
$output = array_filter(explode('_', $test2));
print_r($output); // Results: 'Array ( [0] => 123 [1] => 1256 )'
The problem is evident too, more complicated paterns like Foo2foo12foo1 would pass the filter. And here's where I got a bit stuck.
Recap:
Extract a variable ammount of chunks of numbers from string.
The string contains at least 1 number, and may contain other numbers
and letters separated by underscores.
Only numbers not preceded or followed by letters must be extracted.
Only the numbers in the first half of the string matter.
Since only the first half is needed I decided to split in the first occurrence of letter or mixed number-letter with preg_split():
$test2 = '123_123_234_1Foo2'
$output = preg_split('/([0-9]{1,}[a-zA-Z]{1,})|[^0-9_]/', $test, 2);
preg_match_all('/[0-9]{1,}/', $output[0], $matches);
print_r($matches[0]); // Results: 'Array ( [0] => 123 [1] => 123 [2] => 234 )'
The point of my question is if is there a simpler, safer or more efficient way to achieve this result.
If I understand your question correctly, you want to split an underscore-delimited string, and filter out any substrings that are not numeric. If so, this can be achieved without regex, with explode(), array_filter() and ctype_digit(); e.g:
<?php
$str = '123_123_234_1Foo2';
$digits = array_filter(explode('_', $str), function ($substr) {
return ctype_digit($substr);
});
print_r($digits);
This yields:
Array
(
[0] => 123
[1] => 123
[2] => 234
)
Note that ctype_digit():
Checks if all of the characters in the provided string are numerical.
So $digits is still an array of strings, albeit numeric.
Hope this helps :)
Getting just the numeric part of the string after the explode
$test2 = "123_123_234_1Foo2";
$digits = array_filter(explode('_', $test2 ), 'is_numeric');
var_dump($digits);
Result
array(3) { [0]=> string(3) "123" [1]=> string(3) "123" [2]=> string(3) "234" }
Use strtok
Regex isn't a magic bullet, and there are FAR simpler fixes for your problem, especially considering you're trying to split on a delimiter.
Any of the following approaches would be cleaner, and more maintainable, and the strtok() approach would probably perform better:
Use explode to create and loop through an array, checking each value.
Use preg_split to do the same, but with more a adaptable approach.
Use strtok, as it is designed exactly for this use-case.
Basic exmple for your case:
function strGetInts(string $str, str $delim) {
$word = strtok($str, $delim);
while (false !== $word) {
if (is_integer($word) {
yield (int) $word;
}
$word = strtok($delim);
}
}
$test2 = '123_1256_Foo';
foreach(strGetInts($test2, '_-') as $key {
print_r($key);
}
Note: the second argument to strtok is string containing ANY delimiter to split the string on. Thus, my example will group results into strings separated by underscores or dashes.
Additional Note: If and only if the string only needs to be split on a single delimiter (underscore only), a method using explode will likely result in better performance. For such a solution, see the other answer in this thread: https://stackoverflow.com/a/46937452/1589379 .

Split string on semicolons immediately followed by 7 digital characters

I have a string like this:
2234323,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433;4534453,23,23,44,433;
3,23,44,433;23,23,44,433;23,23,44,433
7545455,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433
As you see, there are semicolons between values. I want to split this string based on 'only semicolons before 7 digits values' so I should have this:
>2234323,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433
>4534453,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433;
>7545455,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433
the only thing that I can think of is explode(';',$string) but this returns this:
>2234323,23,23,44,433;
>3,23,44,433;
>23,23,44,433;
>23,23,44,433
>4534453,23,23,44,433;
>3,23,44,433;
>23,23,44,433;23,23,44,433;
>7545455,23,23,44,433;
>3,23,44,433;23,23,44,433;
>23,23,44,433
Is there any fast method to split string with this format based on the ";" before 7 digits values?
You can use preg_split for that:
$s = '2234323,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433;4534453,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433;7545455,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433';
var_dump(preg_split('/(;\d{7},)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE));
Your output will be
array(5) {
[0] =>
string(58) "2234323,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433"
[1] =>
string(9) ";4534453,"
[2] =>
string(50) "23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433"
[3] =>
string(9) ";7545455,"
[4] =>
string(50) "23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433"
}
I think that the next thing (combine the 1st and 2nd and then 3rd and 4th elements) is not a big deal :)
Let me know if you still here problems here.
You could do a find and replace on numbers that are seven digits long, to insert a token that you can use to split. The output may need a little extra filtering to get to your desired format.
<?php
$in =<<<IN
2234323,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433;4534453,23,23,44,433;
3,23,44,433;23,23,44,433;23,23,44,433
7545455,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433
IN;
$out = preg_replace('/([0-9]{7})/', "#$1", $in);
$out = explode('#', $out);
$out = array_filter($out);
var_export($out);
Output:
array (
1 => '2234323,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433;',
2 => '4534453,23,23,44,433;
3,23,44,433;23,23,44,433;23,23,44,433
',
3 => '7545455,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433',
)
Your input structure seems a little unstable, but once it is stabilized, just use preg_split() to match (and consume) semicolons that are immediately followed by exactly 7 digits. \b is a word boundary to ensure that their is no 8th digit.
Code: (Demo)
$string = <<<STR
2234323,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433;4534453,23,23,44,433;
3,23,44,433;23,23,44,433;23,23,44,433
7545455,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433
STR;
$string = preg_replace('/;?\R/', ';', $string); // I don't know if this is actually necessary for your real project
var_export(
preg_split('/;(?=\d{7}\b)/', $string)
);
Output:
array (
0 => '2234323,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433',
1 => '4534453,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433',
2 => '7545455,23,23,44,433;3,23,44,433;23,23,44,433;23,23,44,433',
)

Divide PHP string into arrayed substrings based on "divider" characters

What I'd like to do is split a PHP string into a set of sub-strings grouped into arrays based on "divider" characters that begin those sub-strings. The characters *, ^, and % are reserved as divider characters. So if I have the string "*Here's some text^that is meant*to be separated^based on where%the divider characters^are", it should be split up and placed in arrays like so:
array(2) {
[0] => "*Here's some text"
[1] => "*to be separated"
}
array(3) {
[0] => "^that is meant"
[1] => "^based on where"
[2] => "^are"
}
array(1) {
[0] => "%the divider characters"
}
I'm totally lost on this one. Does anyone know how to implement this?
You don't ask for $matches[0] so unset it if you want:
preg_match_all('/(\*[^*^%]+)|(\^[^*^%]+)|(%[^*^%]+)/', $string, $matches);
$matches = array_map('array_filter', $matches);
print_r($matches);
The array_filter() removes the empties from the capture group sub-arrays to give the array shown in the question

order a php array by a sub string

I have an array:
Array
(
[0] => 20140929102023_taxonomies.zip
[1] => 20140915175317_taxonomies.zip
[2] => 20140804112307_taxonomies.zip
[3] => 20141002162349_taxonomies.zip
)
I'd like order this array by first 14 characters of strings, that represents a date.
I'd like an array like this:
Array
(
[0] => 20140804112307_taxonomies.zip
[1] => 20140915175317_taxonomies.zip
[2] => 20140929102023_taxonomies.zip
[3] => 20141002162349_taxonomies.zip
)
Thanks.
The sort() function with the natural sorting algorithm should give you the result you are looking for. It's as simple as this.
sort($array, SORT_NATURAL);
This will updat the existing $array variable, you do not need to store the return of the sort function. It simply returns true or falseon success and failure.
The sort function will update the keys as well, if for some reason you need to maintain the keys, and just update the order, you can use asort().
asort($array, SORT_NATURAL);
PHP has tons of ways to sort arrays, you can find the manual for that here.
There is no need to use natural sorting algorithms. A normal sort() would produce the effect you desire, as it will compare each string "starting from the left". For example "20141002162349_taxonomies.zip" is bigger than "20140929102023_taxonomies" because the fifth character (the first digit of the month) is 1 in the first and 0 in the second (and 1 > 0, even in a string - comparison works with ASCII code points).
So:
<?php
$array = array('20141002162349_taxonomies.zip', '20140929102023_taxonomies.zip', '20140804112307_taxonomies.zip', '20140915175317_taxonomies.zip');
sort($array);
var_dump($array);
Result:
array(4) {
[0]=>
string(29) "20140804112307_taxonomies.zip"
[1]=>
string(29) "20140915175317_taxonomies.zip"
[2]=>
string(29) "20140929102023_taxonomies.zip"
[3]=>
string(29) "20141002162349_taxonomies.zip"
}

Categories