I have a function that gets filenames from a directory, and then puts them in an array. All the filenames start with a code, as an example: -0-filename.php, -1-filename.php, -2-filename.php and -10-filename.php.
After some steps, the filenames are echoed out. The process looks like this:
rsort( $archiveArray );
$amount = count( $archiveArray );
$i = 0;
while( $i <= $amount )
{
echo $archiveArray[$i];
$i++;
}
Anyways. The problem is; when I get 10 files in the directory, and try to do the echoing process above, I get the names in a wrong order. - It's supposed to be :
-10-filename.php
-9-filename.php
-8-filename.php
...
-1-filename.php
-0-filename.php
But instead, I get
-9-filename.php
-8-filename.php
...
-10-filename.php
-1-filename.php
-0-filename.php
What's the quickest and easiest way to fix this?
EDIT:
If it wasn't obvious, the filenames are not always identical, even when not including the codes. The are always in this format: -number-randomtext.php, where number is always one higher than the last one, and randomtext can really be anything.
rsort has an optional second parameter called sort_flags. use SORT_NATURAL to do a "natural sort".
rsort($archiveArray, SORT_NATURAL);
Using rsort with SORT_NATURAL works if you're running PHP 5.4. If you're not, however:
natsort( $archiveArray );
$archiveArray = array_reverse( $archiveArray );
...will do the same thing. Just use natsort to do the natural order sorting, then reverse the array.
Test code (PHP 5.3.3):
php > $array = array( '-1-blah', '-2-foo', '-12-boo', '-11-yaay', '-3-bar' );
php > natsort( $array );
php > print_r( $array );
Array
(
[0] => -1-blah
[1] => -2-foo
[4] => -3-bar
[3] => -11-yaay
[2] => -12-boo
)
php > $array = array_reverse( $array );
php > print_r( $array );
Array
(
[0] => -12-boo
[1] => -11-yaay
[2] => -3-bar
[3] => -2-foo
[4] => -1-blah
)
php >
Another way to do this is usort, and write your own sorting criteria.
I used regular expressions to parse the numeric value from the string, as follows:
$files = array(
'-0-filename.php',
'-1-filename.php',
'-2-filename.php',
'-3-filename.php',
'-4-filename.php',
'-5-filename.php',
'-6-filename.php',
'-7-filename.php',
'-8-filename.php',
'-9-filename.php',
'-10-filename.php',
'-11-filename.php',
);
usort($files, 'CustomFileSequence');
var_dump($files);
function CustomFileSequence($a, $b)
{
$pattern = '/^\\-([0-9]*)\\-/';
preg_match($pattern, $a, $matches);
$a_val = $matches[1];
preg_match($pattern, $b, $matches);
$b_val = $matches[1];
if ($a_val < $b_val)
return 1;
if ($a_val > $b_val)
return -1;
if ($a_val == $b_val)
return 0;
}
Output is:
array
0 => string '-11-filename.php' (length=16)
1 => string '-10-filename.php' (length=16)
2 => string '-9-filename.php' (length=15)
3 => string '-8-filename.php' (length=15)
4 => string '-7-filename.php' (length=15)
5 => string '-6-filename.php' (length=15)
6 => string '-5-filename.php' (length=15)
7 => string '-4-filename.php' (length=15)
8 => string '-3-filename.php' (length=15)
9 => string '-2-filename.php' (length=15)
10 => string '-1-filename.php' (length=15)
11 => string '-0-filename.php' (length=15)
You need to convert the number to an int value. Right now it's being interpreted as a string. You can just do something like this:
$names = // your array of file names.
$sorted = // the sorted version names.
for($i = 0; $i < count($arr); $i++) {
$sorted[substr($names[$i], 1, strpos($names[$i], '-', 2))] = $names[$i];
}
print_r($sorted);
Basically, you take the name from $names, give it an index in the sorted array based on the number between the two -s, and add it to $sorted.
I used usort() to do this:-
$files = array('-10-filename.php', '-9-filename.php', '-8-filename.php', '-10-filename.php', '-1-filename.php', '-0-filename.php');
$cmp = function($a, $b){
list($mt, $num1, $name) = explode('-', $a);
list($mt, $num2, $name) = explode('-', $b);
return $num1 - $num2;
};
usort($files, $cmp);
var_dump($files);
Gave me this output:-
array
0 => string '-0-filename.php' (length=15)
1 => string '-1-filename.php' (length=15)
2 => string '-8-filename.php' (length=15)
3 => string '-9-filename.php' (length=15)
4 => string '-10-filename.php' (length=16)
5 => string '-10-filename.php' (length=16)
If you want them sorted in reverse just change the last line in the comparison function to:-
return $num2 - $num1;
As an aside, you may find a comparison of PHP sort functions useful.
Related
This question already has answers here:
How add a link on comma separated multidimensional array
(2 answers)
Closed 7 months ago.
I am trying to generate a string from an array. Need to concatenate the array values with a small string AFTER the value. It doesn't work for the last value.
$data = array (
1 => array (
'symbol' => 'salad'
),
2 => array (
'symbol' => 'wine'
),
3 => array (
'symbol' => 'beer'
)
);
$symbols = array_column($data, 'symbol');
$string_from_array = join($symbols, 'bar');
echo($string_from_array);
// expected output: saladbar, winebar, beerbar
// output: saladbar, winebar, beer
You can achieve it a few different ways. One is actually by using implode(). If there is at least one element, we can just implode by the delimiter "bar, " and append a bar after. We do the check for count() to prevent printing bar if there are no results in the $symbols array.
$symbols = array_column($data, "symbol");
if (count($symbols)) {
echo implode("bar, ", $symbols)."bar";
}
Live demo at https://3v4l.org/ms5Ot
You can also achieve the desired result using array_map(), as follows:
<?php
$data = [
1 => ['symbol' => 'salad'],
2 => ['symbol' => 'wine'],
3 => ['symbol' => 'beer']
];
echo join(", ", array_map(
fn($v) => "{$v}bar",
array_column($data, 'symbol')
)
);
See live code
Array_map() takes every element of the array resulting from array_column() pulling out the values from $data and with an arrow function, appends the string "bar". Then the new array yielded by array_map has the values of its elements joined with ", " to form the expected output which is then displayed.
As a recent comment indicated you could eliminate array_column() and instead write code as follows:
<?php
$data = [
1 => ['symbol' => 'salad'],
2 => ['symbol' => 'wine'],
3 => ['symbol' => 'beer']
];
echo join(", ", array_map(
fn($row) => "{$row['symbol']}bar",
$data
)
);
See live code
Note while this 2nd way, may appear more direct, is it? The fact is that as array_map iterates over $data, the arrow function contains code that requires dereferencing behind the scenes, namely "$row['symbol']".
The join() function is an alias of implode() which
Returns a string containing a string representation of all the array
elements in the same order, with the glue string between each element.
So you need to add the last one by yourself
$data = array (
1 => array (
'symbol' => 'salad'
),
2 => array (
'symbol' => 'wine'
),
3 => array (
'symbol' => 'beer'
)
);
$symbols = array_column($data, 'symbol');
$string_from_array = join($symbols, 'bar');
if(strlen($string_from_array)>0)
$string_from_array .= "bar";
echo($string_from_array);
You can use array_column and implode
$data = array (
1 => array (
'symbol' => 'salad'
),
2 => array (
'symbol' => 'wine'
),
3 => array (
'symbol' => 'beer'
)
);
$res = implode("bar,", array_column($data, 'symbol'))."bar";
Live Demo
Try this:
$symbols = array_column($data, 'symbol');
foreach ($symbols as $symbol) {
$symbol = $symbol."bar";
echo $symbol;
}
btw, you can't expect implode to do what you expect, because it places "bar" between the strings, and there is no between after the last string you get from your array. ;)
Another way could be using a for loop:
$res = "";
$count = count($data);
for($i = 1; $i <= $count; $i++) {
$res .= $data[$i]["symbol"] . "bar" . ($i !== $count ? ", " : "");
}
echo $res; //saladbar, winebar, beerbar
Php demo
I have an example array with filenames:
$test_array = array (
'video-start-1537482914-stop-1537483670.zip',
'video-start-1537533156-stop-1537534299.zip',
'video-start-1537534300-stop-1537534630.zip',
'video-start-1537090052-stop-1537091001.zip'
);
I want to get start timestamp from each file and then sort them in array. I tried with preg_match but it only works for strings, not arrays. How can i achieve that?
public function testSort()
{
$test_array = array (
'video-start-1537482914-stop-1537483670.zip',
'video-start-1537533156-stop-1537534299.zip',
'video-start-1537534300-stop-1537534630.zip',
'video-start-1537090052-stop-1537091001.zip'
);
usort($test_array, function($a, $b) {
return ((int)explode('-',$a)[2] < (int)explode('-',$b)[2]) ? -1 : 1;
});
foreach($test_array as &$line) {
echo $line . PHP_EOL;
}
}
Try this:
<?php
$test_array = array(
'video-start-1537482914-stop-1537483670.zip',
'video-start-1537533156-stop-1537534299.zip',
'video-start-1537534300-stop-1537534630.zip',
'video-start-1537090052-stop-1537091001.zip'
);
foreach ($test_array as $item){
preg_match('/video-start-(.*?)-stop-/', $item, $match);
$timespan[] = $match[1];
}
//sorts an associative array in ascending order.
asort($timespan);
var_dump($timespan);
?>
Output start timestamp:
array (size=4)
3 => string '1537090052' (length=10)
0 => string '1537482914' (length=10)
1 => string '1537533156' (length=10)
2 => string '1537534300' (length=10)
I have an array with corresponding value.
Array
(
[0] => BBsma=200
[1] => SMAperiod=300
[2] => SMA1=400
[3] => SMA2=500
[4] => EMAperiod=300
[5] => EMA1=24
[6] => EMA2=8
)
Now I want to match a certain string like for example BBsma that should return 200. Any help?
Got the array using these codes.
$txt = file_get_contents('INDICATORS.txt');
$rows = explode("\n", $txt);
array_shift($rows);
INDICATORS.txt content
BBperiod=100
BBsma=200
SMAperiod=300
SMA1=400
SMA2=500
EMAperiod=300
EMA1=24
EMA2=8
After you explode your text to the lines use this code:
for($i=0;$i<sizeof($rows);$i++)
{
$temp=explode("=",$rows[$i]);
if(sizeof($temp)==2)
{
$arr[$temp[0]]=$temp[1];
}
}
You will have named array in $arr
if you want to cast second part to int, you just change 6-line to this:
$arr[$temp[0]]=intval($temp[1]);
You could iterate over every line of your array and find the value with a regular match.
Code:
$txt = file_get_contents('INDICATORS.txt');
$rows = explode("\n", $txt);
/*
$rows = [
"BBsma=200",
"SMAperiod=300",
"SMA1=400",
"SMA2=500",
"EMAperiod=300",
"EMA1=24",
"EMA2=8",
];
*/
foreach ($rows as $k=>$v) {
if (preg_match("/(BBsma|SMAperiod|EMAperiod)=([0-9]+)/", $v, $matches)) {
echo "found value " . $matches[2] . " for entry " . $matches[1] . " in line " . $k . PHP_EOL;
}
}
Output:
found value 200 for entry BBsma in line 0
found value 300 for entry SMAperiod in line 1
found value 300 for entry EMAperiod in line 4
You can explode by new line as PHP_EOL like this
$col = "BBsma";
$val = "";
foreach(explode(PHP_EOL,$str) as $row){
$cols = explode("=",$row);
if(trim($cols[0]) == $col){
$val = $cols[1];
break;
}
}
echo "Value $col is : $val";
Live Demo
If your going to use the array a few times, it may be easier to read the file into an associative array in the first place...
$rows = [];
$file = "INDICATORS.txt";
$data = file($file, FILE_IGNORE_NEW_LINES);
foreach ( $data as $item ) {
$row = explode("=", $item);
$rows [$row[0]] = $row[1];
}
echo "EMA1 =".$rows['EMA1'];
This doesn't do the array_shift() but not sure why it's used, but easy to add back in.
This outputs...
EMA1 =24
I think that using array filter answers your question the best. It returns an array of strings with status code 200. If you wanted to have better control later on and sort / search through codes. I would recommend using array_walk to create some sort of multi dimensional array. Either solution will work.
<?php
$arr = [
"BBsma=200",
"SMAperiod=300",
"SMA1=400",
"SMA2=500",
"EMAperiod=300",
"EMA1=24",
"EMA2=8",
];
$filtered = array_filter($arr,"filter");
function filter($element) {
return strpos($element,"=200");
}
var_dump($filtered); // Returns array with all matching =200 codes: BBSMA=200
Output:
array (size=1)
0 => string 'BBsma=200' (length=9)
Should you want to do more I would recommend doing something like this:
///////// WALK The array for better control / manipulation
$walked = [];
array_walk($arr, function($item, $key) use (&$walked) {
list($key,$value) = explode("=", $item);
$walked[$key] = $value;
});
var_dump($walked);
This is going to give you an array with the parameter as the key and status code as it's value. I originally posted array_map but quickly realized array walk was a cleaner solution.
array (size=7)
'BBsma' => string '200' (length=3)
'SMAperiod' => string '300' (length=3)
'SMA1' => string '400' (length=3)
'SMA2' => string '500' (length=3)
'EMAperiod' => string '300' (length=3)
'EMA1' => string '24' (length=2)
'EMA2' => string '8' (length=1)
Working with the array becomes a lot easier this way:
echo $walked['BBsma']; // 200
$anything = array("BBsma"=>"200", "SMAperiod"=>"300", "SMA1"=>"400");
echo "the value is " . $anything['BBsma'];
This will return 200
I have two large arrays of scraped product names and prices similar to the following:
$one=array('grape'=>'0.40','apple'=>'1.20','banana'=>'1.80','lemon'=>'10.43');
$two=array('grappe'=>'1.20','kiwi'=>'7.54','banaana'=>'3.20','aubergine'=>'2.32');
I am attempting to iterate over the arrays using the similar_text function to return the keys that match eachother closely. For example i would like to extract the values of 'grappe'=>'1.20' and 'banaana'=>'3.20' from the above example.
I am unsure how to reference the arrays and pass them to the similar_text function as this function only accepts string data. I presume i will need to correctly reference the arrays using a foreach loop and use an if statement in conjunction with the similar_text function to specify the desired percentage of similarity between the two matches.
For example (within the foreach loop):
if ($result[] = (similar_text( $one, $two)) > 80) {
var_dump($result[]);
}
similar_text( $one, $two) Returns the number of matching chars in both strings so To get percentage you should run similar_text($one, $two, $percent); insted
Example
$one = array('grape' => '0.40','apple' => '1.20','banana' => '1.80','lemon' => '10.43');
$two = array('grappe' => '1.20','kiwi' => '7.54','banaana' => '3.20','aubergine' => '2.32');
$result = array();
foreach ( $one as $key => $value ) {
foreach ( $two as $twoKey => $twoValue ) {
similar_text($key, $twoKey, $percent);
if ($percent > 80) {
$result[$key] = array($value,$twoValue);
}
}
}
var_dump($result);
Output
array
'grape' =>
array
0 => string '0.40' (length=4)
1 => string '1.20' (length=4)
'banana' =>
array
0 => string '1.80' (length=4)
1 => string '3.20' (length=4)
Given a facebook URL with this format:
....&app_data=eid~423423|pid~23982938|admin~1
I want an array:
[ 'eid' => '423423', 'pid => '23982938', 'admin' => '1' ];
This is how I'm doing it:
$app_data = $signed_request['app_data'];
parse_str(str_replace('~','=',str_replace('|','&',$app_data)), $app_data_params);
Is there a better way to achieve this?
You can use preg_replace to eliminate the two calls to str_replace, but I doubt you'd see any performance benefit to doing so. There's nothing wrong with the way you're already doing it.
$str = 'eid~423423|pid~23982938|admin~1';
parse_str(
preg_replace(
array('/\~/','/\|/'),
array('=','&'),
$str
),
$app_data_params
);
print_r($app_data_params);
// Array ( [eid] => 423423 [pid] => 23982938 [admin] => 1 )
Documentation
preg_replace - http://php.net/manual/en/function.preg-replace.php
parse_str - http://php.net/manual/en/function.parse-str.php
Try explode()
$appData = explode("|", $signed_request['app_data']);
That will at least give you
array(3) {
[0] => "eid~4234234",
[1] => "pid~23982398",
[2] => "admin~1"
}
$_GET['app_data']= "eid~423423|pid~23982938|admin~1";
$a = str_replace('~','=', $_GET['app_data']);
$b = str_replace("|","&", $a);
parse_str($b);
echo $eid;
echo $pid;
echo $admin;
There is another way. Gaaah, #Chris beat me to it!
// incoming GET string
$_GET['app_data']= "eid~423423|pid~23982938|admin~1";
$bits = explode("|", $_GET['app_data']);
foreach( $bits as $bit ){
$res = explode('~', $bit);
$result[$res[0]] = $res[1];
}
var_dump($result);
array
'eid' => string '423423' (length=6)
'pid' => string '23982938' (length=8)
'admin' => string '1' (length=1)
Not sure that is any faster though.