PHP count # of items in an object of a given property value - php

How can I count the number of items with the same folder_id# ?
Here's my list of items:
item_id=1, folder_id=1
item_id=2, folder_id=1
item_id=3, folder_id=2
item_id=4, folder_id=3
Here's my UPDATED code:
foreach($items as $item)
{
if(????) //count of $item->folder_id > 1
{
//do something to $item->folder_id=1/$item->item_id=1
}
elseif(????) // cases where $item->item_id != $item->folder_id
{
//do something else to $item->folder_id=1/$item->item_id=2
}
else
{
//do something else to $item->folder_id=2/$item->item_id=3 and folder_id=3/item_id=4
}
}
I'm interested in code that can tell me that the count for folder_id=1 is 2, the count for folder_id=2 is 1, and the count for folder_id=3 is also 1.
UPDATE: I've changed the code above to now include an elseif() because it didn't quite ask all the things I was interested in. Besides counting the # of each folder_id, I'm also interested in distinguishing cases where folder_id != item_id. This would put item_id=1, item_id=2, item_id=3/4 in their own conditional clauses and wouldn't lump item_id=1 and item_id=2 as before.
Any assistance would be greatly appreciated, thank you,

If you had an array of folder_ids, you could use array_count_values to provide exactly the result you need. So let's make an array like that out of an array of objects using array_map:
$callback = function($item) { return $item->folder_id; };
$result = array_count_values(array_map($callback, $items));
This will end up with $result being
array(
1 => 2,
2 => 1,
3 => 1,
);
You can also write the callback inline for an one-liner, or if you are on PHP < 5.3 you can write it as a free function or alternatively using create_function.
See it in action (version for PHP < 5.3).
Update: follow up
Given $results from above, your loop would be:
foreach($results as $folder_id => $count) {
if($count > 1) {
// There were $count items with folder_id == $folder_id
}
// else blah blah
}

$totals = array();
foreach($items as $item) {
$totals[$item->folder_id]++;
}

function count_items(Array $items, $folder_id) {
return count(array_filter($items,
function($item) use($folder_id) {
return $item->folder_id === $folder_id;
}
));
}

I think this is what you are looking for.
function folder_count($items, $num)
{
$cnt = 0;
foreach($items as $item)
{
if($item->folder_id > $num)
{
$cnt++;
}
}
return $cnt;
}
foreach ($items as $item)
{
if (folder_count($items, 1))
{
//do something to $item->folder_id=1/$item->item_id=1, item_id=2
}
else
{
//do something else to $item->folder_id=2/$item->item_id=3 and folder_id=3/item_id=4
}
}

Related

PHP. Counting number of strings satisfying an specific condition in a foreach loop

I'm trying to find the number of strings that satisfy some conditions in a foreach loop. This is what I've tried so far:
<?php
$list = $item->getProperty();
$n = 0;
foreach($list as $single) {
$designation = $single->getPropertyName(); // var_dump($designation); outputs 150 strings
if (strpos($designation, 'foo') === 0) { // var_dump($designation); outputs 5 strings containing 'foo' in their designation names
$n++;
echo count($n);
}
}
?>
echo count($n); returns 11111 instead of returning 5 which is the value I want to obtain.
Could someone help me out a bit?
<?php
$list = $item->getProperty();
$n = 0;
foreach($list as $single) {
$designation = $single->getPropertyName(); // var_dump($designation); outputs 150 strings
if (strpos($designation, 'foo') === 0) { // var_dump($designation); outputs 5 strings containing 'foo' in their designation names
$n++;
}
}
echo $n;
If you would like to achieve the same in a more functional and elegant way, you could use array_reduce
array_reduce($item->getProperty(), function($sum, $single) {
if (strpos($single->getPropertyName(), 'foo') === 0) {
$sum++;
}
return $sum;
});
A less readable, but more elegant one-line solution would look like:
array_reduce($item->getProperty(), function($sum, $single) { return (strpos($single->getPropertyName(), 'foo') === 0) ? ++$sum : $sum; }
And now that we have short arrow functions in PHP, you can use this if you are running on PHP 7.4:
array_reduce($item->getProperty(),
fn($sum, $single) => (strpos($single->getPropertyName(), 'foo') === 0) ? ++$sum : $sum);

PHP Resource for getting combinations of x distinct items in y distinct bins?

Does anyone know a resource (manual or book) or have the PHP solution for getting all the combinations of x distinct items in y distinct bins?
For example, if I had 2 items [1, 2] with 2 bins, the 4 possibilities would be:
[ 1,2 ] [ ]
[ 1 ] [ 2 ]
[ 2 ] [ 1 ]
[ ] [ 1,2 ]
I need the combinations, not permutations, as order of items is irelevent. And there is no min/max for items in a bin. And if you're going to downgrade my question because it's unclear, please specify what you're confused with. I've spent the entire day trying to find a solution, even in another programming language. Apparently, not very easy to come up with.
UPDATE: Hi Karol, thanks for the comment and link. I'm still working away on this, and did find that page in my searches and converted that to PHP here:
function combinationsOf($k, $xs){
if ($k === 0)
return array(array());
if (count($xs) === 0)
return array();
$x = $xs[0];
$xs1 = array_slice($xs,1,count($xs)-1);
$res1 = combinationsOf($k-1,$xs1);
for ($i = 0; $i < count($res1); $i++) {
array_splice($res1[$i], 0, 0, $x);
}
$res2 = combinationsOf($k,$xs1);
return array_merge($res1, $res2); }
I'm going about it in a different way with this than what I originally hoped for, so still hoping to hear from someone ... thanks!
UPDATE: So I'm making progress, making use of the above recursive function along with another link I found: Permutation Of Multidimensional Array in PHP
Although, correct me if I'm wrong (it's been a loooong day), but it's not permutations, but combinations, that's being generated here.
You could use a backtracking method which utilizes recursion. Basically, it's like a "smart brutforce" approach which takes a path and tries to get combinations which work.
The solution may look a little large but most of the functions are there just to support the combo function. The main brains behind the algorithm is behind the combo function which creates the combinations. The rest of the functions are there to support the combo function and print a nice looking output.
<?php
function toPlainArray($arr2) {
$output = "[";
foreach($arr2 as $arr) {
$output .= "[";
foreach($arr as $val) {
$output .= $val . ", ";
}
if($arr != []) {
$output = substr($output, 0, -2) . "], ";
} else {
$output .= "], ";
}
}
return substr($output, 0, -2) . "]";
}
function difference($arr2d, $arr1d) {
foreach((array)$arr2d as $arr) {
foreach($arr as $item) {
if(in_array($item, $arr1d)) {
$index = array_search($item, $arr1d);
unset($arr1d[$index]);
}
}
}
return $arr1d;
}
function getNextPossibleSol($pSol, $item) { // returns an array (1d)
$allItems = range(1, $item);
return difference($pSol, $allItems);
}
function createEmpty2dArray($arr, $amount) {
for($i = 0; $i < $amount; $i++) {
$arr[] = [];
}
return $arr;
}
function isSmallerThenPartialItems($item, $pSol) {
foreach($pSol as $arr) {
foreach($arr as $val) {
if($val > $item) return false;
}
}
return true;
}
function combo($items, $buckets, $partialSol=[]) {
if($partialSol == []) { // Starting empty array, populate empty array with other arrays (ie create empty buckets to fill)
$partialSol = createEmpty2dArray($partialSol, $buckets);
}
$nextPossibleSol = getNextPossibleSol($partialSol, $items);
if($nextPossibleSol == []) { // base case: solution found
echo toPlainArray($partialSol); // 2d array
echo "<br /><br />";
} else {
foreach($nextPossibleSol as $item) {
for($i = 0; $i < count($partialSol); $i++) {
if(isSmallerThenPartialItems($item, $partialSol)) { // as order doesn't matter, we can use this if-statement to remove duplicates
$partialSol[$i][] = $item;
combo($items, $buckets, $partialSol);
array_pop($partialSol[$i]);
}
}
}
}
}
combo(2, 2); // call the combinations functions with 2 items and 2 buckets
?>
Output:
[[1, 2], []]
[[1], [2]]
[[2], [1]]
[[], [1, 2]]

How can I repeat a specific iteration in a foreach loop in PHP?

As there is no iterator in PHP, the only way to loop through an array without getting the length of the array is to use foreach loop.
Let say I have the following loop:
foreach ($testing_array as $testing_entry) {
$result = my_testing_api_call($testing_entry);
if ($result == 'server dead')
break;
else if ($result == 'done') {
// do something to handle success code
continue;
}
else {
sleep(5);
// I want to retry my_testing_api_call with current $testing entry, but don't know what to write
}
}
One way to do that is to use for loop instead.
for ( $i=0; $i < count($testing_array); $i++ ) {
$result = my_testing_api_call($testing_entry[$i]);
if ($result == 'server dead')
break;
else if ($result == 'done') {
// do something to handle success code
continue;
}
else {
sleep(5);
$i--; //so it will repeat the current iteration.
}
}
The problem is that the $testing_array is not originally using number as index, so I have to do some data massage to use a for loop. Is there a way I can repeat a specific iteration in a foreach loop?
Perhaps a do-while will work for you.
Untested Code:
foreach ($testing_array as $testing_entry) {
do {
$result = my_testing_api_call($testing_entry);
if ($result == 'server dead') {
break 2; // break both loops
} elseif ($result == 'done') {
// do something to handle success code
} else {
sleep(5);
// I want to retry my_testing_api_call with current $testing entry, but don't know what to write
}
} while ($result !== 'done');
}
Or a single loop structure that destroys the input array as it iterates.
Untested Code:
$result = '';
while ($testing_array && $result !== 'server dead') {
$result = my_testing_api_call(current($testing_array));
if ($result == 'done') {
// do something to handle success code
array_shift($testing_array);
} elseif ($result !== 'server dead') {
sleep(5); // I want to retry my_testing_api_call with current $testing entry, but don't know what to write
}
}
Or you can use your for loop by indexing $test_array with array_values() if you don't need the keys in your // do something.
$testing_array = array_values($testing_array);
for ($i=0, $count=count($testing_array); $i < $count; ++$i) {
$result = my_testing_api_call($testing_entry[$i]);
if ($result == 'server dead') {
break;
} else if ($result == 'done') {
// do something to handle success code
} else {
sleep(5);
--$i; //so it will repeat the current iteration.
}
}
If you do need the keys down script, but you want to use for, you could store an indexed array of keys which would allow you to use $i to access the keys and maintain data synchronicity.
My final suggestion:
Use while (key($testing_array) !== null) {...} to move the pointer without destroying elements.
Code: (Demo)
$array1 = [
"one" => 1,
"two" => 2,
"three" => 3,
"four" => 4
];
while (key($array1)!==null) { // check if the internal pointer points beyond the end of the elements list or the array is empty
$current = current($array1);
$key = key($array1);
echo "$key => $current\n"; // display current key value pair
if ($current < 3) { // an arbitrary condition
echo "\t";
$array1[$key] = ++$current; // increment the current value
} else { // current value is satisfactory
echo "\t(advance pointer)\n";
next($array1); // move pointer
}
}
Output:
one => 1
one => 2
one => 3
(advance pointer)
two => 2
two => 3
(advance pointer)
three => 3
(advance pointer)
four => 4
(advance pointer)
You are trying to handle two different things in your loop, that makes it hard to write clean control flow. You could separate the retry-logic from the result handling:
function call_api_with_retry($entry) {
do {
if (is_defined($result)) sleep(5);
$result = my_testing_api_call($entry);
} while ($result != 'done' && $result != 'server dead');
return $result;
}
foreach ($testing_array as $testing_entry) {
$result = call_api_with_retry($testing_entry);
if ($result == 'server dead')
break;
else if ($result == 'done') {
// do something to handle success code
continue;
}
}
(there might be syntax errors, it's been a while since I wrote PHP code)
To repeat a single specific iteration you need to add a control mechanism, it is not an intended behavior after all.
There are many suggestions here, all of them are kinda over-engineered.
PHP is a high level derivate of C and both languages have the 'goto' operator, it has a bad reputation because people have historically used it too much.
In the end a foreach/while loop is internally nothing else than a 'goto' operation.
Here is the code:
foreach ($array as $key => $value)
{
restart:
if ($value === 12345) goto restart;
}
That's how this should be done just keep in mind that this can cause an endless loop, like in the example.
Look at the next complicated version without goto:
foreach ($array as $key => $value) while(1)
{
if ($value === 12345) continue;
break;
}
This is essentially the same as the goto, just with more code.
If you'd want to "break" or "continue" the foreach loop you'd write "break 2" or "continue 2"

php foreach on array when arrays might be nested

The following code uses foreach on an array and if the value is an array it does a for each on the nested array
foreach ($playfull as $a)
{
if (is_array($a))
{
foreach ($a as $b)
{
print($b);
print("<p>");
}
} else {
print($a);
print("<p>");
}
}
This only works if you know that the arrays may only be nested one level deep
If arrays could be nested an unknown number of levels deep how do you achieve the same result? (The desired result being to print the value of every key in every array no matter how deeply nested they are)
You can use array_walk_recursive. Example:
array_walk_recursive($array, function (&$val)
{
print($val);
}
This function is a PHP built in function and it is short.
Use recursive functions (that are functions calling themselves):
function print_array_recursively($a)
{
foreach ($a as $el)
{
if (is_array($el))
{
print_array_recursively($el);
}
else
{
print($el);
}
}
}
This is the way, print_r could do it (see comments).
You want to use recursion, you want to call your printing function in itself, whenever you find an array, click here to see an example
$myArray = array(
"foo",
"bar",
"children" => array(
"biz",
"baz"),
"grandchildren" => array(
"bang" => array(
"pow",
"wow")));
function print_array($playfull)
{
foreach ($playfull as $a)
{
if (is_array($a))
{
print_array($a);
} else {
echo $a;
echo "<p>";
}
}
}
echo "Print Array\n";
print_array($myArray);
You could use a recursive function, but the max depth will be determined by the maximum nesting limit (see this SO question, Increasing nesting functions calls limit, for details about increasing that if you need it)
Here's an example:
$array = array(1,array(2,3,array(4,5)),6,7,8);
function printArray($item)
{
foreach ($item as $a)
{
if (is_array($a))
{
printArray($a);
} else {
print($a);
print("<p>");
}
}
}
printArray($array);
I hope that helps.
Try this -
function array_iterate($arr, $level=0, $maxLevel=0)
{
if (is_array($arr))
{
// unnecessary for this conditional to enclose
// the foreach loop
if ($maxLevel < ++$level)
{ $maxLevel = $level; }
foreach($arr AS $k => $v)
{
// for this to work, the result must be stored
// back into $maxLevel
// FOR TESTING ONLY:
echo("<br>|k=$k|v=$v|level=$level|maxLevel=$maxLevel|");
$maxLevel= array_iterate($v, $level, $maxLevel);
}
$level--;
}
// the conditional that was here caused all kinds
// of problems. so i got rid of it
return($maxLevel);
}
$array[] = 'hi';
$array[] = 'there';
$array[] = 'how';
$array['blobone'][] = 'how';
$array['blobone'][] = 'are';
$array['blobone'][] = 'you';
$array[] = 'this';
$array['this'][] = 'is';
$array['this']['is'][] = 'five';
$array['this']['is']['five'][] = 'levels';
$array['this']['is']['five']['levels'] = 'deep';
$array[] = 'the';
$array[] = 'here';
$var = array_iterate($array);
echo("<br><br><pre>$var");

Foreach loop with extra filter -> need to identify last entry

I am trying to get the last entry in a foreach loop but struggling so far. My biggest problem is that once inside the foreach loop, I need to filter the array by eliminating every author who has 0 posts. This is my code so far:
$authors = get_users('orderby=nicename');
foreach ($authors as $author ) {
if ( count_user_posts( $author->id ) >= 1 ) { IF LAST {special <li>} else {normal <li> }
Can anyone shed some light on how to get the last entry in this scenario? Thanks
You should write a function to filter the array and then you can pop the last item off of it like this:
// use this section of code in PHP >= 5.3 - utilizes anonymous function
$filtered_array = array_filter($authors, function($author) {
if(count_user_posts($author->id) >= 1) return true;
return false;
});
// use this section of code for older versions of PHP - uses regular function
function author_filter($author) {
if(count_user_posts($author->id) >= 1) return true;
return false;
}
$filtered_array = array_filter($authors, 'author_filter');
// the rest is the same regardless of PHP version
$last_author = array_pop($filtered_array);
// output
foreach($filtered_array as $author) {
// regular output
}
// then do special output for $last_author
<?php
$numItems = count($arr);
echo "<ul>";
for($i = 0; $i < $numItems; $i++) {
if(as_zero_post){
//$i++;
continue;
}
$i == $numItems-1 ? "<li class='last-item'>".$arr[$i]."</li>" : "<li class='normal-item'>".$arr[$i]."</li>";
}
echo "</ul>"
?>

Categories