PHP foreach with counter and IF statement - php

I am trying to use a FOREACH loop in PHP to output different messages depending on the position of the value in the array. My code is below:
$qtyValidFiles = count($validFiles);
$tick = 0;
foreach($validFiles AS $validItem)
{
if($tick = 0)
{
echo"'#$validItem,";
}
elseif($tick < $qtyValidFiles -1 && $tick != 0)
{
echo"#$validItem,";
}
elseif($tick = $qtyValidFiles -1)
{
echo"#$validItem'";
}
}; $tick++;
In this instance, $qtyValidFiles has 4 values.
The code above returns:
#Games'#Movies'#Music'#TV'
which suggests to me that $tick is always being seen as value 3.

If $validFiles is an indexed array, you can do:
foreach ($validFiles as $tick => $validItem)
instead of incrementing $tick yourself.
Your entire loop is unnecessary, though. You can do:
echo "'" . implode(",", array_map(function($x) { return "#$x"; }, $validFiles)) . "'";
This should be better than your code because you don't do the right thing if the array has exactly 1 item; you'll print the beginning quote and a comma, but I think you really want the one element surrounded by quotes.

$tick is always 0 because the $tick++; is outside of the loop. Move it up a line.
Also as Rahul Kumar states, use == as the comparison operator.
Or just do this, no loop needed:
echo '#' . implode(',#', $validFiles);

Seems like the simpler and correct equivalent to your code would be this:
$qtyValidFiles = count($validFiles);
$str = '';
foreach($validFiles AS $validItem) {
$str .= "#$validItem,";
}
if (!empty($str)) {
echo "'" . substr($str, 0, -1) . "'"; //removes the last ","
}

Related

regex not working as expected when matching file names with wildcards

I am writing a PHP function that takes an array of file names and removes file names from the array if they do not match a set of criteria input by the user. The function iterates through the array and compares each value to a regex. The regex is formed by inserting variables from user input. If the user didn't specify a variable, regex wildcard characters are inserted in the variable's place. The file names are all very systematic, like 2020-06-N-1.txt so I know exactly how many characters to expect in the file names and from the user input. However, when I run the code, file names that don't match the regex are still in the array. Some non-matching file names are taken out, but many others are left in. Parts of my PHP code are below. Any help is appreciated.
function fileFilter() {
global $fileArray, $fileFilterPattern;
/* The loop starts at 2 and goes to count()-1 because the first 2 elements were removed
earlier with unset */
for ($j = 2; $j < count($fileArray) - 1; $j++) {
if(!(preg_match($fileFilterPattern, $fileArray[$j]))) {
unset($fileArray[$j]);
}
}
return;
}
// If user does not provide a filter value, it gets converted into wildcard symbol
if ($month == '') {
$month = '..';
}
if ($year == '') {
$year = '....';
}
if ($section == '') {
$section = '.';
}
$fileFilterPattern = "/{$year}-{$month}-{$section}-.\.txt/";
/* function only runs if user applied at least one filter */
if (!($month == '..' && $year == '....' && $section == '.')) {
fileFilter();
}
Below I have included an example of how the array contains elements that aren't matches. I obtain my output array using echo json_encode($fileArray);
My input:
month is ""
year is ""
section is "L"
Expected result:
Array contains only files that have L in the section spot (YEAR-MONTH-**SECTION**-NUMBER.txt)
Resulting array:
{"8":"2020-06-L-1.txt","9":"2020-06-L-2.txt","10":"2020-06-L-3.txt","11":"2020-06-L-4.txt","12":"2020-06-L-5.txt","15":"2020-06-N-3.txt","16":"2020-06-N-4.txt","17":"2020-06-N-5.txt","18":"2020-06-N-6.txt","19":"2020-06-O-1.txt","20":"2020-06-O-2.txt","21":"2020-06-O-3.txt","22":"2020-06-O-4.txt","23":"2020-06-S-1.txt","24":"2020-06-S-2.txt","25":"2020-06-S-3.txt"}
The problem is using unset() inside a loop. On the next iteration, the index is no longer the same as it was before you messed with the array using unset(). Sometimes, you deal with this by using array_values(), but in this case it's simpler to just build a second array that has only the values you want. The following code works. I've used array_values() just to take the string that you provided and get the indexes back to normal.
That said, since the "first 2 elements were removed
earlier with unset" you need to run array_values() on the array before you get to this part.
<?php
$str ='{"8":"2020-06-L-1.txt","9":"2020-06-L-2.txt","10":"2020-06-L-3.txt","11":"2020-06-L-4.txt","12":"2020-06-L-5.txt","15":"2020-06-N-3.txt","16":"2020-06-N-4.txt","17":"2020-06-N-5.txt","18":"2020-06-N-6.txt","19":"2020-06-O-1.txt","20":"2020-06-O-2.txt","21":"2020-06-O-3.txt","22":"2020-06-O-4.txt","23":"2020-06-S-1.txt","24":"2020-06-S-2.txt","25":"2020-06-S-3.txt"}';
$fileArray = json_decode($str, true);
$fileArray = array_values($fileArray);
echo '<p>fileArray: ';
var_dump($fileArray);
echo '</p>';
function fileFilter() {
global $fileArray, $fileFilterPattern;
$filteredArray = [];
for ($j = 0; $j < count($fileArray); $j++) {
if(preg_match($fileFilterPattern, $fileArray[$j]) === 1) {
//unset($fileArray[$j]);
array_push($filteredArray, $fileArray[$j]);
}
}
echo '<p>filteredArray: ';
var_dump($filteredArray);
echo '</p>';
//return;
}
$month =='';
$year = '';
// If user does not provide a filter value, it gets converted into wildcard symbol
if ($month == '') {
$month = '..';
}
if ($year == '') {
$year = '....';
}
if ($section == '') {
$section = '.';
}
$section = 'L';
$fileFilterPattern = "#{$year}-{$month}-{$section}-.\.txt#";
echo '<p>fileFilterPattern: ';
var_dump($fileFilterPattern);
echo '</p>';
/* function only runs if user applied at least one filter */
if (!($month == '..' && $year == '....' && $section == '.')) {
fileFilter();
}
?>
The main problem is that the count decreases each time you unset, so you should define the count once. Assuming the -1 and $j = 2 are correct for your scenario:
$count = count($fileArray) - 1;
for ($j = 2; $j < $count; $j++) {
if(!(preg_match($fileFilterPattern, $fileArray[$j]))) {
unset($fileArray[$j]);
}
}
There are others ways where you don't have to assume and then keep track of the keys:
foreach($fileArray as $k => $v) {
if(!preg_match($fileFilterPattern, $v)) {
unset($fileArray[$k]);
}
}
I would get rid of your fileFilter function and use this handy function instead, which will return all items that match the pattern:
$fileArray = preg_grep($fileFilterPattern, $fileArray);

PHP Array foreach question

I have a question about arrays and foreach.
If i have an array like this:
$test_arr = array();
$test_arr['name1'] = "an example sentence";
$test_arr['anything'] = "dsfasfasgsdfg";
$test_arr['code'] = "4334refwewe";
$test_arr['empty1'] = "";
$test_arr['3242'] = "";
how can I do a foreach and "pick" only the ones that have values? (in my array example, would only take the first 3 ones, name1, anything and code).
I tried with
foreach ($test_arr as $test) {
if (strlen($test >= 1)) {
echo $test . "<br>";
}
}
but it doesn't work. Without the "if" condition it works, but empty array values are taken into consideration and I don't want that (because I need to do a <br> after each value and I don't want a <br> if there is no value)
Sorry if I don't explain myself very well, I hope you understand my point. Shouldn't be too difficult I guess..
Thanks for your help !
Maybe will work
foreach ($test_arr as $test) {
if (strlen($test)!=="") {
echo $test . "<br>";
}
}
Your solution with corrected syntax:
foreach ($test_arr as $test) {
if (strlen($test)>=1) {
echo $test . "<br>";
}
}
Since empty strings are false, you could just do this (but you'd exclude 0's with the if):
foreach ($test_arr as $key => $val) {
if ($val) {
echo $val. "<br>";
}
}
If it has to be an empty string then (excluding 0 and FALSE):
foreach ($test_arr as $key => $val) {
// the extra = means that this will only return true for strings.
if ($val !== '' ) {
echo $val. "<br>";
}
}
Since it looks like you're using an associative array, you should be able to do this:
foreach( $test_arr as $key => $value )
{
if( $value != "" )
{
echo $value . "<br />";
}
}
As shown, you can test $value for an empty string directly. Since this is precisely the test you are trying to accomplish, I would hope that this would solve your problem perfectly.
On another note, this is pretty straight forward and should be very maintainable in the future when you've forgotten exactly what it was that you were doing!
You are better off to use a while loop like this:
while(list($test_key, $test_value) = each($test_arr))
{
if($test_value != "") { echo $test_value . "<br/>"; }
}
reset($test_arr);
If your array gets large, the while will be much faster. Even on small arrays, I have noticed a big difference in the execution time.
And if you really don't want the array key. You can just do this:
while(list(, $test_value) = each($test_arr))
{
if($test_value != "") { echo $test_value . "<br/>"; }
}
reset($test_arr);
You can check if the value is emtpy with empty().
Note that values like 0 or false are considered empty as well, so you might have to check for string length instead.
just a simple typing error:
foreach ($test_arr as $test) {
if (strlen($test) >= 1) {
echo $test . "<br>";
}
}
Try this:
foreach ($test_arr as $test) {
if (strlen($test) > 0) {
echo $test . "<br>";
}
}

PHP Last Array Key Issue

I have this simple code for pasting images from a directory, I've sorted them into an array but the problem I can't seem to work out is how to get the last array to be different.
This is my code so far:
foreach($images as $image){
echo("{image : '$image'}, ");
}
I'm looking to keep print the single items in the array but on the last one I'd like to not have the comma.
Any help would be great,
Thanks!
Simple.
function doit($image) {
return "{image: '$image'}"
}
$images = array_map('doit',$images);
$images = implode(', ',$images);
echo "{image : '".implode("'}, {image : '",$images)."'}";
Try:
<?php
$buffer = array();
foreach($images as $image){
$buffer[] = "{image : '$image'}";
}
echo implode(', ', $buffer);
Try using the key and the length of the array:
$arrLength = count($images);
foreach($images as $key=>$image){
echo("{image : '$image'}");
if($key < $arrLength - 1){ echo ", "; }
}
Or use an array_map:
function make_array($n)
{
return "{image: '$n'}"
}
$map = array_map("make_array", $images);
$new_array = implode(', ', $map);
You could do this attractively with a do..while loop:
$image = current($images);
do {
echo "{image : '$image'}";
} while (($image = next($images) && (print " ,"));
Note that you have to use print not echo there, as echo does not behave as a function.
The second part of the conditional only executes if the first part passes, so " ," will only be printed if another image exists.
If there is the possibility (as in, even the vaguest possibility) that your array may contain values that aren't non-empty strings, you'll need to be more verbose:
} while (
(false !== ($image = next($images)) &&
(print " ,")
);
I'm not convinced this is very readable, however, even split over multiple lines, so if this is the case I'd go for one of the other approaches.
Either use an if statement and check if it's the last and echo accordingly, or concatenate without echoing, trim the result after it's generated, and echo.
You could do if and else statements, where if its the last image print without comma else if it isn't print with comma.
$last = array_pop(array_keys($images));
foreach($images as $key => $image) {
if ($key == $last) {
... last image ,don't output comma
}
}

How to use a foreach loop, but do something different on the last iteration?

This is probably a simple question, but how do you iterate through an array, doing something to each one, until the last one and do something different?
I have an array of names. I want to output the list of names separated by commas.
Joe, Bob, Foobar
I don't want a comma at the end of the last name in the array, nor if there is only one value in the array (or none!).
Update: I can't use implode() because I have an array of User model objects where I get the name from each object.
$users = array();
$users[] = new User();
foreach ($users as $user) {
echo $user->name;
echo ', ';
}
How can I achieve this and still use these objects?
Update: I was worrying too much about how many lines of code I was putting in my view script, so I decided to create a view helper instead. Here's what I ended up with:
$array = array();
foreach($users as $user) {
$array[] = $user->name;
}
$names = implode(', ', $array);
Use implode:
$names = array('Joe', 'Bob', 'Foobar');
echo implode(', ', $names); # prints: Joe, Bob, Foobar
To clarify, if there is only one object in the array, the ', ' separator will not be used at all, and a string containing the single item would be returned.
EDIT: If you have an array of objects, and you wanted to do it in a way other than a for loop with tests, you could do this:
function get_name($u){ return $u->name; };
echo implode(', ', array_map('get_name', $users) ); # prints: Joe, Bob, Foobar
$array = array('joe', 'bob', 'Foobar');
$comma_separated = join(",", $array);
output: joe,bob,Foobar
Sometimes you might not want to use implode.
The trick then is to use an auxiliary variable to monitor not the last, but the first time through the loop.
vis:
$names = array('Joe', 'Bob', 'Foobar');
$first = true;
$result = '';
foreach ($names as $name)
{
if (!$first)
$result .= ', ';
else
$first = false;
$result .= $name;
}
implode(', ', $array_of_names)
psuedocode....
integer sigh=container.getsize();
sigh--;
integer gosh=0;
foreach element in container
{
if(gosh!=sigh)
dosomething();
else
doLastElementStuff();
gosh++;
}
looking at all the other answers, it seems PHP has gotten a lot more syntactic S since I last wrote anything in it :D
I come accross this a lot building SQL statements etc.
$joiner = " ";
foreach ($things as $thing) {
echo " $joiner $thing \n";
$joiner = ',';
}
FOr some reason its easier to work out the logic if you think of the ",", "AND" or "OR" as an option/attribute that goes before an item. The problem then becomes how to suppress the the "," on the first line.
I personally found the fastest way (if you're into micro optimization) is:
if(isset($names[1])) {
foreach ($names as $name) {
$result .= $name . ', ';
}
$result = substr($result, 0, -2);
} else {
$result = $names[0];
}
isset($names[1]) is the fastest (albeit not so clear) way of checking the length of an array (or string). In this case, checking for at least two elements is performed.
I actually find it easier to create my comma delimited text a little differently. It's a bit more wordy, but it's less function calls.
<?php
$nameText = '';
for ($i = 0; $i < count($nameArray); $i++) {
if ($i === 0) {
$nameText = $nameArray[$i];
} else {
$nameText .= ',' . $nameArray[$i];
}
}
It adds the comma as a prefix to every name except where it's the first element if the array. I have grown fond of using for as opposed to foreach since I have easy access to the current index and therefore adjacent elements of an array. You could use foreach like so:
<?php
$nameText = '';
$nameCounter = 0;
foreach ($nameArray as $thisName) {
if ($nameCounter === 0) {
$nameText = $thisName;
$nameCounter++;
} else {
$nameText .= ',' . $thisName;
}
}

How can I display an array in a human readable format?

If I have an array that looks like this:
$str = '';
if( $_POST['first'] )
$str = $_POST['first'];
if( $_POST['second'] )
$str .= ($str != '' ? ',' : '') . $_POST['second'];
if( $_POST['third'] )
$str .= ($str != '' ? ',' : '') . $_POST['third'];
if( $_POST['fourth'] )
$str .= ($str != '' ? ',' : '') . $_POST['second'];
$str .= ($str != '' ? '.' : '');
Which gives me something like this:
Joe, Adam, Mike.
However, I would like to add an "and" before the last item.
So it would then read:
Joe, Adam, and Mike.
How can I modify my code to do this?
Arrays are awesome for this:
$str = array();
foreach (array('first','second','third','fourth') as $k) {
if (isset($_POST[$k]) && $_POST[$k]) {
$str[] = $_POST[$k];
}
}
$last = array_pop($str);
echo implode(", ", $str) . " and " . $last;
You should probably special case the above for when there's one item. I, in fact, wrote a function called "conjunction" which does the above, and includes the special case:
function conjunction($x, $c="or")
{
if (count($x) <= 1) {
return implode("", $x);
}
$ll = array_pop($x);
return implode(", ", $x) . " $c $ll";
}
Nice question!
Updated: General purpose way to do this:
function and_form_fields($fields)
{
$str = array();
foreach ($fields as $k) {
if (array_key_exists($k, $_POST) && $v = trim($_POST[$k])) {
$str[] = $v;
}
}
return conjunction($str, "and");
}
...
and_form_fields(array("Name_1","Name_2",...));
I added correct $_POST checking to avoid notices, and blank values.
The first ideea that comes to my mind is to always keep a last one name in an auxiliar variabile. When you have another one to put in, you take it put the 'comma' and get the next name in the aux.
At the end, when you finished added the names, you add 'and' and the last name from aux.
Instead of posting each as a separate variable, why not post them as an array:
#pull the array from the POST:
$postedarray = $_POST['names'];
#count the number of elements in the posted array:
$numinarray = count($postedarray);
#subtract 1 from the number of elements, because array elements start at 0
$numinarray = $numinarray -1;
#set a prepend string
$prependstr = "and ";
#Pull the last element of the array
$lastname = $postedarray[$numinarray];
#re-define the last element to contan the prepended string
$postedarray[$numinarray] = "$prependstr$lastname";
#format the array for your requested output
$comma_separated = implode(", ", $postedarray);
print "$comma_separated";

Categories