Remove everything before (including special character) in array - php

Is it possible to remove everything before special characters including this character in array ?
For example, SUBSTR() function is used in string
$a= ('1-160');
echo substr($a,strpos($a,'-')+1);
//output is 160
Any function for array like SUBSTR()? (least priority to preg_replace).
Here array is structured as following example, every index consists int value + hyphen(-)
$a= array('1-160','2-250', '3-380');
I need to change removing every values before hyphen and hyphen also
$a= array('160','250', '380');
Actually my requirement is to sum all values after the hyphen(-) in array. If hyphen(-) can be removed, it can be done as
echo array_sum($a);
//output is 790
but, because of the special characters, I am generating output as following way.
$total = 0;
foreach($a AS $val){
$b = explode('-',$val);
$total += $b[1];
}
echo $total;
//output is 790
I am searching short and fast method as possible.

strstr with substr should work just fine
<?php
$a = ['1-160','2-250', '3-380'];
$result = [];
foreach($a as $b) {
$result[] = substr(strstr($b, '-'), 1); // strstr => get content after needle inclusive needle, substr => remove needle
}
var_dump($result);
var_dump(array_sum($result));
https://3v4l.org/2HsAK

Although you already have the answer, this is for your reference.
array_sum(array_map(function ($item) {return explode('-', $item)[1];}, $a));

Related

PHP sort array by number in filename

I am working on a photo gallery that automatically sorts the photos based on the numbers of the file name.
I have the following code:
//calculate and sort
$totaal = 0;
if($handle_thumbs = opendir('thumbs')){
$files_thumbs = array();
while(false !== ($file = readdir($handle_thumbs))){
if($file != "." && $file != ".."){
$files_thumbs[] = $file;
$totaal++;
}
}
closedir($handle_thumbs);
}
sort($files_thumbs);
//reset array list
$first = reset($files_thumbs);
$last = end($files_thumbs);
//match and split filenames from array values - image numbers
preg_match("/(\d+(?:-\d+)*)/", "$first", $matches);
$firstimage = $matches[1];
preg_match("/(\d+(?:-\d+)*)/", "$last", $matches);
$lastimage = $matches[1];
But when i have file names like photo-Aname_0333.jpg, photo-Bname_0222.jpg, it does start with the photo-Aname_0333 instead of the 0222.
How can i sort this by the filename numbers?
None of the earlier answers are using the most appropriate/modern technique to perform the 3-way comparison -- the spaceship operator (<=>).
Not only does it provide a tidier syntax, it also allows you to implement multiple sorting rules in a single step.
The following snippet break each filename string in half (on the underscore), then compare the 2nd half of both filenames first, and if there is a tie on the 2nd halves then it will compare the 1st half of the two filenames.
Code: (Demo)
$photos = [
'photo-Bname_0333.jpg',
'photo-Bname_0222.jpg',
'photo-Aname_0333.jpg',
'photo-Cname_0111.jpg',
'photo-Cname_0222.jpg',
'photo-Aname_0112.jpg',
];
usort($photos, function ($a, $b) {
return array_reverse(explode('_', $a, 2)) <=> array_reverse(explode('_', $b, 2));
});
var_export($photos);
Output:
array (
0 => 'photo-Cname_0111.jpg',
1 => 'photo-Aname_0112.jpg',
2 => 'photo-Bname_0222.jpg',
3 => 'photo-Cname_0222.jpg',
4 => 'photo-Aname_0333.jpg',
5 => 'photo-Bname_0333.jpg',
)
For anyone who still thinks the preg_ calls are better, I will explain that my snippet is making potentially two comparisons and the preg_ solutions are only making one.
If you wish to only use one sorting criteria, then this non-regex technique will outperform regex:
usort($photos, function ($a, $b) {
return strstr($a, '_') <=> strstr($b, '_');
});
I super-love regex, but I know only to use it when non-regex techniques fail to provide a valuable advantage.
Older and wiser me says, simply remove the leading portion of the string before sorting (use SORT_NATURAL if needed), then sort the whole array. If you are scared of regex, then make mapped calls of strtok() on the underscore.
Code: (Demo)
array_multisort(preg_replace('/.*_/', '', $photos), $photos);
usort is a php function to sort array using values.
usort needs a callback function that receives 2 values.
In the callback, depending of your needs, you will be return the result of the comparision 1, 0 or -1. For example to sort the array asc, I return -1 when the firts value of the callback is less than second value.
In this particular case I obtain the numbers of the filename, and compare it as string, is not necesary to cast as integer.
<?php
$photos=[
'photo-Bname_0222.jpg',
'photo-Aname_0333.jpg',
'photo-Cname_0111.jpg',
];
usort($photos, function ($a, $b) {
preg_match("/(\d+(?:-\d+)*)/", $a, $matches);
$firstimage = $matches[1];
preg_match("/(\d+(?:-\d+)*)/", $b, $matches);
$lastimage = $matches[1];
if ($firstimage == $lastimage) {
return 0;
}
return ($firstimage < $lastimage) ? -1 : 1;
});
print_r($photos);
It sorts alphabetically because you use sort() on the filename. The 2nd part of your code does nothing.
You might want to take a look at usort http://php.net/manual/en/function.usort.php
You can do something like
function cmp($a, $b) {
if ($a == $b) {
return 0;
}
preg_match('/(\d+)\.\w+$/', $a, $matches);
$nrA = $matches[1];
preg_match('/(\d+)\.\w+$/', $b, $matches);
$nrB = $matches[1];
return ($nrA < $nrB) ? -1 : 1;
}
usort($files_thumb, 'cmp');
Also, I'm not sure about your regex, consider a file named "abc1234cde2345xx". The one I used takes the last digits before a file extension at the end. But it all depends on your filenames.
sort(array,sortingtype) , you have to set the second parameter of the sort() function to 1 so it will sort items numerically
//calculate and sort
$totaal = 0;
if($handle_thumbs = opendir('thumbs')){
$files_thumbs = array();
while(false !== ($file = readdir($handle_thumbs))){
if($file != "." && $file != ".."){
$files_thumbs[] = $file;
$totaal++;
}
}
closedir($handle_thumbs);
}
sort($files_thumbs,1);
//reset array list
$first = reset($files_thumbs);
$last = end($files_thumbs);
//match and split filenames from array values - image numbers
preg_match("/(\d+(?:-\d+)*)/", "$first", $matches);
$firstimage = $matches[1];
preg_match("/(\d+(?:-\d+)*)/", "$last", $matches);
$lastimage = $matches[1];

How can I sort an array by two criteria?

I have an array I want to echo alphabetized while ignoring the number that starts each string, as such:
0 Apple
1 Apple
3 Apple
0 Banana
1 Banana
0 Carrot
//...
When I sort, the number is sorted first. So, I've tried asort, sort_string with no success.
$file = file("grades.txt");
asort($file, SORT_STRING);
Can I look only at the alphabet characters and ignore numbers? Or can I ignore the first character and sort starting with the second character? What should I do to get the above result?
It would be great if the numbers could be in order AFTER the arrays are echoed alphabetically, but it is not demanded if too difficult to do.
Maybe try php's uasort function.
http://php.net/manual/en/function.uasort.php
function cmp($a, $b) {
if ($a[2] == $b[2]) {
return 0;
}
return ($a[2] < $b[2]) ? -1 : 1;
}
uasort($array, 'cmp');
You can swap the position of the alphabetic part and numeric part, and use strcmp() to compare the string in usort().
http://php.net/manual/en/function.usort.php
usort($arr, function($a, $b) {
$a = $a[2].' '.$a[0];
$b = $b[2].' '.$b[0];
return strcmp($a, $b);
});
You can use preg_replace() to remove numbers from beginning of strings, preg_replace() accepts third param ( subject ) as an array ( the search and replace is performed on every item ).
$file = preg_replace( '/^[\d\s]+/', '', file("grades.txt") );
arsort( $file );
EDIT:
Use preg_replace( '/^([\d\s]+)(.+)/', '$2 $1', file("grades.txt") ) to shift the numbers to the end of string.
For this you need a custom order function, which you can do with uasort(), e.g.
Simply explode() your string by a space and save the number and the string in a variable. Then if string is the same order the elements by the number. Else sort by the string.
uasort($arr, function($a, $b){
list($numberA, $stringA) = explode(" ", $a);
list($numberB, $stringB) = explode(" ", $b);
if(strnatcmp($stringA, $stringB) == 0)
return $numberA < $numberB ? -1 : 1;
return strnatcmp($stringA, $stringB);
});

How can I trim the last two characters of each key in an array?

This is my array (input):
$value = array("jan01" => "01", "feb02" => "02", "mar03" => "03", "apr04" => "04");
I am using this code to get the array keys:
implode(" ", array_map("ucwords", array_keys($value)));
Now my problem is I want to get all keys by triming the last two characters of each key.
How can I change/modify my code, so that it trim's the last two characters of each key?
EDIT:
I also want to skip first 3 keys, means I don't want the first 3 keys to be trimmed.
I think this should work for you:
Just take the substr() from your key and then use ucwords() on it.
implode(" ",array_map(function($v){
return ucwords(substr($v, 0, -2));
},array_keys($value)));
EDIT:
AS from your updated question you don't want to take the substr from the first 3 elements. So just use a counter variable, e.g.
$counter = 1;
echo implode(" ", array_map(function($v)use(&$counter){
if($counter++ > 3)
return ucwords(substr($v, 0, -2));
return ucwords($v);
},array_keys($value)));
Here's something for undetermined array depth.
$arr = your array;
$trimmed_values = array();
array_walk_recursive($arr, function($key, $value) use (&$trimmed_values)
{
$trimmed_values[] = substr($key, 0, -2);
});
This won't work if you're not using PHP 5.3+ as lower versions don't have anonymous functions.

PHP Using the array_filter

I will try to explain my situation as best as possible so bear with me.
I have an array with single words in them, for example:
This
is
a
test
array
Now i created another array that looks alike but with 2 words, which looks like this:
This is
is a
a test
test array
Ok here is where my problem starts. I have an array of 'common words' those words should be exluded from the array. Let's say those common words would be is and a for this example. Right now i search for common words first on the single word array so i can use if(in_array($word, $common_words)) continue; Which makes it skip the one if it's in the common_words array.
But this would result in this array:
This test
test array
But this is not how i want it to happen. It should be like this:
test array
Because this is the only 1 that had these 2 words next to eachother originally before we started to take out the 'common_words'. (are you still with me?)
The problem here is that if(in_array) doesn't work anymore if I have an array with 2 words. So i did some research and stumbled upon the array_filter command. I think this is what I need but i'm at a total loss as on how to use/apply it to my code.
I hope I explained it well enough for you to understand what my problem is and I would appreciate it if someone could help me.
Thanks in advance!
Your guess is correct, you can use:
$array = ['this is', 'array array', 'an array', 'test array'];
$stop = ['is', 'test'];
$array = array_filter($array, function($x) use ($stop)
{
return !preg_match('/('.join(')|(', $stop).')/', $x);
});
-i.e. exclude all items with certain words in it by pattern using array_filter()
This will work with filtering because it will match by regex, i.e. from $stop we'll get regex (is)|(test)
A good idea will be to evaluate regex separately so do not evaluate it each time inside array_filter() iteration, like:
$array = ['this is', 'array array', 'an array', 'test array'];
$stop = ['is', 'test'];
$pattern = '/('.join(')|(', $stop).')/';
$array = array_filter($array, function($x) use ($pattern)
{
return !preg_match($pattern, $x);
});
Important note#1: if your stop words may contain some special characters that will be treated in regex in special way, it's needed to use preg_quote() like:
$pattern = '/'.join('|', array_map(function($x)
{
return '('.preg_quote($x, '/').')';
}, $stop)).'/';
$array = array_filter($array, function($x) use ($pattern)
{
return !preg_match($pattern, $x);
});
Important note#2: If your array of stopwords is too long this may cause regex compilation fail because of it's length (too large). There are some tricks to overcome it, but if it's your case, you'd better to use strpos() instead:
$array = array_filter($array, function($x) use ($stop)
{
foreach($stop as $word)
{
if(false!==strpos($x, $word))
{
return false;
}
}
return true;
});
I think, the bes way, two operators: array_diff and array_unique
$a[] = 'This';
$a[] = 'is';
$a[] = 'is';
$a[] = 'a';
$a[] = 'a';
$a[] = 'test';
$a[] = 'test';
$a[] = 'array';
$excluded = array('is', 'a');
$result = array_diff($a, $excluded); // Remove all excluded words
$result = array_unique($result); // unique values
var_dump($result);
And result:
array (size=3)
0 => string 'This' (length=4)
5 => string 'test' (length=4)
7 => string 'array' (length=5)

PHP: If number (with comma), convert it to right number format (with point)

I have an array of mixed values:
$row = array('Unspecified risk','Yes','8','3','2','13','none','-1,49','-2,51','-1,46','-1,54'); -1,94 -1,55
As you can see it contains text and both negative and positive comma-numbers.
I need to convert the numeric values to the right number format and leave the text values as is.
Now I'm looping over the values:
foreach ($row as $value) {
// If $value is numeric, convert it to the
// right number format for use with MySQL (decimal(10,2))
// If not, leave it be.
}
Two related questions I've investigated but cannot find a suitable solution.
Converting a number with comma as decimal point to float
I need php regular expression for numeric value including "-" and ","
Could anyone provide a practical example?
You don't need to use regular expressions.
use str_replace() as you need to replace the ',' for a '.', and then use intval() or floatval() functions to get the numeric value. You can also use strstr() to look for the '.' and decide if using intval() or floatval()
Example:
$row = array('Unspecified risk', 'Yes', '8', '3', '2', '13', 'none', '-1,49', '-2,51', '-1,46', '-1,54');
function toNumber($target){
$switched = str_replace(',', '.', $target);
if(is_numeric($target)){
return intval($target);
}elseif(is_numeric($switched)){
return floatval($switched);
} else {
return $target;
}
}
$row = array_map('toNumber', $row);
var_dump($row);
We use str_replace() to replace the dot for the comma, this way it's a international notation float, even if it's on string, this way later on we can check if it's numeric with is_numeric() <-- this function is awesome as it detects from a string if it's a number or not, no matter integer or float etc.
We use the is_numeric to check if the value is integer float or text and return the corresponding value using intval() or floatval() (the value without the replace applied will not return as a valid numeric, only after switching the , and . it will return true as numeric).
We use $row = array_map('toNumber', $row); to apply the changes to the array.
Profit xD
$row = array('Unspecified risk','Yes','8','3','2','13','none','-1,49','-2,51','-1,46','-1,54');
foreach($row as $key => $var) {
if(strstr($var, ",") && !is_numeric($var)) {
$var1 = str_replace(",","", $var);
if(is_numeric($var1)) {
$decimal = strstr($var, ',', TRUE);
$digits = str_replace($decimal, "", $var1);
$finalValue = $digits * pow(10,$decimal);
$row[$key] = $finalValue;
}
}
}
echo "<pre>"; print_r($row);
NOTE: This will work for php 5.3 or php 5.3+
I am responding so many years later because I dont find any of the provided solutions reliable enough.
This is a more rigid test if a string contains a float with a comma as decimal separator, and if yes, converts it. Also it removes spaces around the number if needed. It does not really test for malformed numbers.
if( preg_match('/^\s*[0-9\.]*,\d*\s*$/', $str))
return (float)str_replace(",",".",str_replace(".","",trim($str)));
else
return $str ;
If you know a numerical value ALWAYS has a European notation (so 1.234 without a comma should also be coverted to 1234), it should be:
if( preg_match('/^\s*[0-9\.]*,?\d*\s*$/', $str))
return (float)str_replace(",",".",str_replace(".","",trim($str)));
else
return $str ;
If you want a truly rigid test, no malformed numbers (like starting with a thousands separator), and that uses ONLY three digits between thousands separators (not all countries use three, though, like India!):
if( preg_match('/^\s*\d{0,3}((?<=\d)\.\d{3})*,?\d*\s*$/', $str))
return (float)str_replace(",",".",str_replace(".","",trim($str)));
else
return $str ;
And lastly if you want to go even more rigid and also do not want numbers to be able to begin or end with a comma ("1.234," or ",17", which in some cases are considered correct) it becomes
if( preg_match('/^\s*\d{1,3}((?<=\d)\.\d{3})*(,\d+)*\s*$/', $str))
return (float)str_replace(",",".",str_replace(".","",trim($str)));
else
return $str ;
Use is_numeric to test and number_format to format:
foreach ($row as &$value) {
$number = str_replace(',', '.', $value);
if (is_numeric($number)) {
$value = number_format($number, 2, '.', '');
}
}
unset($value);

Categories