associative array pointers / traversing associative array - php

Does next() and prev() work on associative arrays?
I'm trying to traverse through a dataset that uses two records to describe one "game" if you will. So when I'm on the second record w/ matching id i need to look at the record before and grab eg_item['final_score'].
{"id":"75", "team_name":"TEAM1", "home_team_name":"TEAM1", "image":"TEAM1_HOME.png", "final_score":"37"},
{"id":"75", "team_name":"TEAM2", "home_team_name":"TEAM2", "image":"TEAM2_AWAY.png", "final_score":"10"},
{"id":"76", "team_name":"TEAM1", "home_team_name":"TEAM1", "image":"TEAM1_HOME.png", "final_score":"10"},
{"id":"76", "team_name":"TEAM2", "home_team_name":"TEAM2", "image":"TEAM2_AWAY.png", "final_score":"14"},
All of the examples I'm finding use lame array('one','two',three') type examples that just don't help.
code sample:
foreach( $json_output as $eg_item ) :
if( $this_game_id == $last_game_id ) :
// get this records info
$b_score = $eg_item['final_score'];
$b_team_name = $eg_item['team_name'];
prev( $json_output );
// get previous records info
$a_score = $eg_item['final_score'];
$a_team_name = $eg_item['team_name'];
$a_game_id = $eg_item['id'];
// put pointer back
next( $json_output );
else :
// skip next record
endif;
endforeach;

That looks like an array of associative arrays, right? If so, loop through them by index 2 at a time, then you can look at the current inner array and the previous inner array in each iteration of the loop:
for ($i = 1; $i < count($array); $i+= 2) {
$current = $array[$i];
$last = $array[$i-1];
//$current['final_score'], $last['final_score'], etc
}

Yes they work just fine, heres a small test case i have done:
<?php
$sample = array(
"first" => Array("a","d","c"),
"second" => Array("a","d","c"),
"third" => Array("a","d","c"),
"fourth" => Array("a","d","c")
);
while(next($sample))
{
$item = current($sample);
echo key($sample) . "\n";
if(is_array($item))
{
echo "\t" . current($item) . "\n";
while(next($item))
{
echo "\t" . current($item) . "\n";
}
}
}
?>
output: http://codepad.org/2ZeqzWcx

Related

Finding the first, last and nth row in a foreach loop

I was wondering if PHP has a gracefull method to find the first, last and/or nth row in a foreach loop.
I could do it using a counter as follows:
$i = 0;
$last = count($array)-1;
foreach ($array as $key => $row) {
if ($i == 0) {
// First row
}
if ($i == $last) {
// Last row
}
$i++;
}
But somehow this feels like a bit of a dirty fix. Any solutions or suggestions?
Edit
As suggested in the comments I moved the count($array) outside the loop.
foreach ($array as $key => $row) {
$index = array_search($key, array_keys($array));
if ($index == 0) {
// First row
}
if ($index == count($array) - 1) {
// Last row
}
}
In php we have current and end function to get first and last value of array.
<?php
$transport = array('foot', 'bike', 'car', 'plane');
echo $first = current($transport); // 'foot';
echo $end = end($transport); // 'plane';
?>
Modified :
Easy way without using current or end or foreach loop:
$last = count($transport) - 1;
echo "First : $transport[0]";
echo "</br>";
echo "Last : $transport[$last]";
Using Arrays
For the first element in an array you can simply seek $array[0];. Depending on the array cursor you can also use current($array);
For the middle of an array you can use a combination of array_search() and array_keys().
For the end of an array you can use end($array); noting that this aslso moves the array cursor to the last element as well (as opposed to simply returning the value).
Using Iterators
However ArrayIterator's may also work well in your case:
The first element is available at ArrayIterator::current(); once constructed. (If you're halfway through the iterator you'll need to reset().)
For the n'th or a middle element you can use an undocumented Iterator::seek($index); method.
For the last element you can use a combination of seek() and count().
For example:
$array = array('frank' => 'one',
'susan' => 'two',
'ahmed' => 'three');
$arrayobject = new ArrayObject($array);
$iterator = $arrayobject->getIterator();
// First:
echo $iterator->current() . PHP_EOL;
// n'th: (taken from the documentation)
if($iterator->valid()){
$iterator->seek(1); // expected: two, output: two
echo $iterator->current() . PHP_EOL; // two
}
// last:
$iterator->seek(count($iterator)-1);
echo $iterator->current() . PHP_EOL;
$arr = ["A", "B", "C", "D", "E"];
reset($arr);
// Get First Value From Array
echo current($arr);
// Get Last Value From Array
echo end($arr);
Visit below link for details of above used functions.
reset() : http://php.net/manual/en/function.reset.php
current() : http://php.net/manual/en/function.current.php
end() : http://php.net/manual/en/function.end.php

PHP how to output the top array_count_values value from array?

I have a table that I need to find "top trusted builder" from, Trusts are separated by " ; ", So I need to pull all the data, explode the usernames, order and count them, and Im stuck on finally outputting the top username form the array, I have posted up the full segment of PHP, because im sure there has to be a much better way of doing this.
<?php
$GTB = $DBH->query('SELECT builders from griefprevention_claimdata where builders <> ""');
$GMCB->setFetchMode(PDO::FETCH_ASSOC);
$buildersarray = array();
while($row = $GTB->fetch()) {
$allbuilders = explode(";", $row['builders']);
for($i = 0; $i < count($allbuilders); $i++)
{
$buildersarray[] = $allbuilders[$i];
}
}
echo "<pre>";
print_r(array_count_values(array_filter($buildersarray)));
echo "</pre>";
?>
Outputs
Array
(
[username1] => 4
[username2] => 1
[username3] => 1
)
You can return the array from the array_count_values command into a new array called $topbuilders, and then echo out the current (first) record:
$topbuilders = array_count_values(array_filter($buildersarray));
echo current(array_keys($topbuilders)).', Rank = '.current($topbuilders);
current() will return the first item in an associative array.
Instead of using array_count_values() it would be better to use the names as the key to a frequency array:
$GTB = $DBH->query('SELECT builders from griefprevention_claimdata where builders <> ""');
$result = array();
while (($builders = $GTB->fetchColumn()) !== false) {
foreach (explode(";", $builders) as $builder) {
if (isset($result[$builder])) {
$result[$builder]++;
} else {
$result[$builder] = 1;
}
}
// sort descending based on numeric comparison
arsort($result, SORT_NUMERIC);
echo "Name = ", key($result), " Count = ", current($result), "\n";

For Each Key Isn't A Number

So I'm trying to iterate through an XML feed and paginate it but I've run into a problem. When I try to get the index (key) of the current array it outputs a string "campDetails" on every iteration instead of an incrementing integer like 0,1,2,3
Here is a sample of the XML format
<campaigns>
<campDetails>
<campaign_id>2001</campaign_id>
<campaign_name>Video Chat Software</campaign_name>
<url>http://www.fakeurl.com</url>
</campDetails>
<?php
$call_url = "https://www.fakeurl.com";
if($xml = simplexml_load_file($call_url, "SimpleXMLElement", LIBXML_NOCDATA)):
foreach($xml as $i => $offers):
$offer_link = $offers->url;
$offer_raw_name = $offers->campaign_name;
echo $i . " " . $offer_link; ?> </br> <?php echo $offer_raw_name;
endforeach;
endif;
?>
Output to be expected:
0 http://www.fakeurl.com
Video Chat Software
Actual output:
campDetails http://www.fakeurl.com
Video Chat Software
EDIT: Thank you for your answers everyone. It seems I was given incorrect information from another question on here. I was told that $i would keep a numerical index for the current iteration.
print_r($xml); (obviously more results but this is the first)
SimpleXMLElement Object ( [campDetails] => Array ( [0] => SimpleXMLElement Object ( [campaign_id] => 2001 [campaign_name] => Video Chat Software [url] => http://www.fakeurl.com/ )
simplexml_load_file returns an object, a foreach loop will iterate over its fields, and $i will hold the current field's key.
If you want a numeric counter as well, just increment on your own:
$j = 0;
foreach($xml as $i => $offer){
// do your stuff
$j++;
}
Its a bit redundant, but you can use something like:
$x = 0;
foreach($xml as $i => $offers):
// other stuff
echo $x . " " . $offer_link; ?> </br> <?php echo $offer_raw_name;
$x++;
endforeach;
You can also streamline your echo line like so:
echo "$i $offer_link <br> $offer_raw_name";
In this usage of foreach, $i is not a numeric index but a key. In this case, the key for the current object is campDetails.
Alternative Code
$i = 0;
foreach($xml as $offer) {
// your code...
++$i;
}
For more details on the type of object you receive from simplexml_load read the docs or debug with print_r($xml);.
Your $i is not an index. You could do something like:
$index = 0;
foreach($xml as $i => $offers):
$offer_link = $offers->url;
$offer_raw_name = $offers->campaign_name;
echo $index++ . " " . $offer_link; ?> </br> <?php echo $offer_raw_name;
endforeach;
Instead of using the current foreach and associative array, I pushed all values to an indexed array.

Reverse order of foreach list items

I would like to reverse the order of this code's list items. Basically it's a set of years going from oldest to recent and I am trying to reverse that output.
<?php
$j=1;
foreach ( $skills_nav as $skill ) {
$a = '<li><a href="#" data-filter=".'.$skill->slug.'">';
$a .= $skill->name;
$a .= '</a></li>';
echo $a;
echo "\n";
$j++;
}
?>
Walking Backwards
If you're looking for a purely PHP solution, you can also simply count backwards through the list, access it front-to-back:
$accounts = Array(
'#jonathansampson',
'#f12devtools',
'#ieanswers'
);
$index = count($accounts);
while($index) {
echo sprintf("<li>%s</li>", $accounts[--$index]);
}
The above sets $index to the total number of elements, and then begins accessing them back-to-front, reducing the index value for the next iteration.
Reversing the Array
You could also leverage the array_reverse function to invert the values of your array, allowing you to access them in reverse order:
$accounts = Array(
'#jonathansampson',
'#f12devtools',
'#ieanswers'
);
foreach ( array_reverse($accounts) as $account ) {
echo sprintf("<li>%s</li>", $account);
}
Or you could use the array_reverse function.
array_reverse() does not alter the source array, but returns a new array. (See array_reverse().) So you either need to store the new array first or just use function within the declaration of your for loop.
<?php
$input = array('a', 'b', 'c');
foreach (array_reverse($input) as $value) {
echo $value."\n";
}
?>
The output will be:
c
b
a
So, to address to OP, the code becomes:
<?php
$j=1;
foreach ( array_reverse($skills_nav) as $skill ) {
$a = '<li><a href="#" data-filter=".'.$skill->slug.'">';
$a .= $skill->name;
$a .= '</a></li>';
echo $a;
echo "\n";
$j++;
}
Lastly, I'm going to guess that the $j was either a counter used in an initial attempt to get a reverse walk of $skills_nav, or a way to count the $skills_nav array. If the former, it should be removed now that you have the correct solution. If the latter, it can be replaced, outside of the loop, with a $j = count($skills_nav).
If you don't mind destroying the array (or a temp copy of it) you can do:
$stack = array("orange", "banana", "apple", "raspberry");
while ($fruit = array_pop($stack)){
echo $fruit . "\n<br>";
}
produces:
raspberry
apple
banana
orange
I think this solution reads cleaner than fiddling with an index and you are less likely to introduce index handling mistakes, but the problem with it is that your code will likely take slightly longer to run if you have to create a temporary copy of the array first.
Fiddling with an index is likely to run faster, and it may also come in handy if you actually need to reference the index, as in:
$stack = array("orange", "banana", "apple", "raspberry");
$index = count($stack) - 1;
while($index > -1){
echo $stack[$index] ." is in position ". $index . "\n<br>";
$index--;
}
But as you can see, you have to be very careful with the index...
You can use usort function to create own sorting rules
Assuming you just need to reverse an indexed array (not associative or multidimensional) a simple for loop would suffice:
$fruits = ['bananas', 'apples', 'pears'];
for($i = count($fruits)-1; $i >= 0; $i--) {
echo $fruits[$i] . '<br>';
}
If your array is populated through an SQL Query consider reversing the result in MySQL, ie :
SELECT * FROM model_input order by creation_date desc
If you do not have Boolean false values in your array, you could use next code based on internal pointer functions:
$array = ['banana', 'apple', 'pineapple', 'lemon'];
$value = end($array);
while ($value !== false) {
// In case you need a key
$key = key($array);
// Do something you need to
echo $key . ' => ' . $value . "\n";
// Move pointer
$value = prev($array);
}
This solution works for associative arrays with arbitrary keys and do not require altering existing or creating a new one.
<?php
$j=1;
array_reverse($skills_nav);
foreach ( $skills_nav as $skill ) {
$a = '<li><a href="#" data-filter=".'.$skill->slug.'">';
$a .= $skill->name;
$a .= '</a></li>';
echo $a;
echo "\n";
$j++;
}
?>

PHP Count values of an array

Is there a way to count the values of a multidimensional array()?
$families = array
(
"Test"=>array
(
"test1",
"test2",
"test3"
)
);
So for instance, I'd want to count the 3 values within the array "Test"... ('test1', 'test2', 'test3')?
$families = array
(
"Test"=>array
(
"test1",
"test2",
"test3"
)
);
echo count($families["Test"]);
I think I've just come up with a rather different way of counting the elements of an (unlimited) MD array.
<?php
$array = array("ab", "cd", array("ef", "gh", array("ij")), "kl");
$i = 0;
array_walk_recursive($array, function() { global $i; return ++$i; });
echo $i;
?>
Perhaps not the most economical way of doing the count, but it seems to work! You could, inside the anonymous function, only add the element to the counted total if it had a non empty value, for example, if you wanted to extend the functionality. An example of something similar could be seen here:
<?php
$array = array("ab", "cd", array("ef", "gh", array("ij")), "kl");
$i = 0;
array_walk_recursive($array, function($value, $key) { global $i; if ($value == 'gh') ++$i; });
echo $i;
?>
The count method must get you there. Depending on what your actual problem is you maybe need to write some (recursive) loop to sum all items.
A static array:
echo 'Test has ' . count($families['test']) . ' family members';
If you don't know how long your array is going to be:
foreach($families as $familyName => $familyMembers)
{
echo $familyName . ' has got ' . count($familyMembers) . ' family members.';
}
function countArrayValues($ar, $count_arrays = false) {
$cnt = 0;
foreach ($ar as $key => $val) {
if (is_array($ar[$key])) {
if ($count_arrays)
$cnt++;
$cnt += countArrayValues($ar);
}
else
$cnt++;
}
return $cnt;
}
this is custom function written by me, just pass an array and you will get full count of values. This method wont count elements which are arrays if you pass second parameter as false, or you don't pass anything. Pass tru if you want to count them also.
$count = countArrayValues($your_array);

Categories