get key and value of second last element of an array - php

i have an array of undefined size, for example :
<?php
$array["foo"] = 86 ;
$array["bar"] = 49 ;
$array["matt"] = 96 ;
?>
i don't want to disturb array's internal pointer , but want to get a COPY of second last value of array instead.

I don't know, why you use a map, when in fact you want an ordered list instead, but
$tmp = array_values($array);
echo $tmp[count($tmp) -2];
should do it. With php5.4 this should work either
echo array_values($array)[count($array)-2];

I'm not sure what size your array is planned for, so copying all values into a separate array might not be a good idea.
The following code slices out an array of length 1 just from the second last position and sets $key and $value.
$pair = array_slice($array, -2, 1, true);
$key = key($pair);
$value = current($pair);
PS: Should probably be put into a simple separated function?!

You can do it this way.
$array["foo"] = 86 ;
$array["bar"] = 49 ;
$array["matt"] = 96 ;
$x = count($array);
foreach($array as $row)
{
if($x == 2)
{ $secondLast = $row;}
$x--;
}
echo $secondLast;
Because you are using associative array.

Related

How do I move a group of items in a PHP array below the next group of items?

In an Array with normal numerical keys ( $file_array_for_editing ), I want to move a set of consecutive X number of values below the next set of X values.
Example: If a group of X values is 10, then in an Array of 50, I have 5 sets of 10 values. I want to move the 10 values in set 3 - below the 10 values in set 4.
So that : Set1(10 values) , Set2(10 values) , Set3(10 values) , Set4(10 values) , Set5(10 values)
Becomes: Set1(10 values) , Set2(10 values) , Set4(10 values) , Set3(10 values) Set5(10 values)
Since I don't know a better way, this is how I have done it now - by what I believe is switching the values:
$file_array_for_editing = [];
for($i=1; $i <= 50; $i++) {
$file_array_for_editing[] = $i;
}
$number_in_one_set = 10 ;
$set_number_to_move = 3 ;
$f = $file_array_for_editing ;
$s = ($set_number_to_move - 1) * $number_in_one_set ; // (3 - 1) * 10 = 20
list(
$f[$s+0],$f[$s+1],$f[$s+2],$f[$s+3],$f[$s+4],$f[$s+5],$f[$s+6],$f[$s+7],$f[$s+8],$f[$s+9],$f[$s+10],$f[$s+11],$f[$s+12],$f[$s+13],$f[$s+14],$f[$s+15],$f[$s+16],$f[$s+17],$f[$s+18],$f[$s+19]
) =
[$f[$s+10],$f[$s+11],$f[$s+12],$f[$s+13],$f[$s+14],$f[$s+15],$f[$s+16],$f[$s+17],$f[$s+18],$f[$s+19],$f[$s+0],$f[$s+1],$f[$s+2],$f[$s+3],$f[$s+4],$f[$s+5],$f[$s+6],$f[$s+7],$f[$s+8],$f[$s+9]];
$file_array_for_editing = $f ;
At the end, the numerical keys stay properly in order, from 0 to 49.
I need help finding a way to do this where I can vary the $number_in_one_set
If possible, I would appreciate help finding a solution where it is clearly apparent what is being done, so that after a year, when I look at the code again, it will be easy to understand. (as in, even now, I just vaguely understand why the list() above works)
Possibly following might help you on your way. You can change the 'sets' you'd like to switch using array $switch.
<?php
$arr = [];
for($i=1; $i <= 50; $i++) {
$arr[] = $i;
}
$set = 10; // each set has 10 items
$switch = [3, 4]; // switch element 3 vs. 4
$newArr = array_chunk($arr, $set); // chop array in chunks of size $set
// swap
$tmp = $newArr[$switch[0]]; // $temp=$newArr[3]
$newArr[$switch[0]] = $newArr[$switch[1]]; // $newArr[3]=newArr[4]
$newArr[$switch[1]] = $tmp; // $newArr[4]=$newArr[3]
$result = [];
array_walk_recursive($newArr, function($el) use (&$result) { $result[] = $el; }); // flatten array
EDIT
array_walk_recursive() applies the callback function (function($el)) to each element of nested array $newArr.
Each element of array $newArr, referenced by $el will be pushed into array $result, a flat indexed array.
Also, because the anonymous function (closure) writes to flat array $result which is defined in the parent scope, we need to employ the use language construct.
source manual: Closures may also inherit variables from the parent scope. Any such variables must be passed to the use language construct.
working demo
#jibsteroos - I really like the method you provided (which #CBroe also suggested) - reason being that it allows any two sets anywhere within the original array to be swapped.
After some thought, I find I prefer using the method below, even though it is a little long winded - reason being my use case is only to exchange a set with the one below it.
<?php
$arr = []; // Setting up a sample array
for($i=1; $i <= 50; $i++) {
$arr[] = $i;
}
$set_size = 10 ; // 10 items per set
$set = 3 ; // set to move down
$set_to_move = ($set - 1) * $set_size; // start position of this set in the array - 20
print_r($arr) ;
echo '<hr>';
$slice1 = array_slice($arr,0,$set_to_move);
print_r($slice1) ; // from the start of the array - to the start of the set to move
echo '<hr>';
$slice2 = array_slice($arr,$set_to_move,$set_size);
print_r($slice2) ; // the set to move
echo '<hr>';
$slice3 = array_slice($arr,$set_to_move + $set_size,$set_size);
print_r($slice3) ; // the set after the set to move
echo '<hr>';
$slice4 = array_slice($arr,$set_to_move + (2 * $set_size));
print_r($slice4) ; // the rest of the array
echo '<hr>';
$arr_edited = array_merge($slice1,$slice3,$slice2,$slice4,);
print_r($arr_edited) ; // All 4 arrays combined in the new order
echo '<hr>';

Effective way to get array index and value names

I have a 0 indexed array that I can't do much about, but inside this array there are values that I need to echo. example array is:
$x = array(0 => array('store'=> 107));
I would like to have 2 variables that both echo texts store and 107
I could do this, using
$var1 = array_keys($x[0]);
$var2 = array_values($x[0]);
echo $var1[0]; // store
echo $var2[0]; // 107
I would like to know if there is a more effective way of getting those values, or remving that first 0 index. as array_filter($x) or unset($x) obviously don't work as in other cases.
As an alternative, you could also use combinations of key() and reset() if you're curious.
$x = array(0 => array('store'=> 107));
$y = reset($x); // point to first element
$key = key($y); // get the current key, store
$val = reset($y); // get the value
echo $key; // store
echo $val; // 107
this should work for you.
$x = array(0 => array('store'=> 107));
foreach($x as $y){
foreach ($y as $key => $value){
echo $key;
echo $value;
}
}

how to skip elements in foreach loop

I want to skip some records in a foreach loop.
For example, there are 68 records in the loop. How can I skip 20 records and start from record #21?
Five solutions come to mind:
Double addressing via array_keys
The problem with for loops is that the keys may be strings or not continues numbers therefore you must use "double addressing" (or "table lookup", call it whatever you want) and access the array via an array of it's keys.
// Initialize 25 items
$array = range( 1, 25, 1);
// You need to get array keys because it may be associative array
// Or it it will contain keys 0,1,2,5,6...
// If you have indexes staring from zero and continuous (eg. from db->fetch_all)
// you can just omit this
$keys = array_keys($array);
for( $i = 21; $i < 25; $i++){
echo $array[ $keys[ $i]] . "\n";
// echo $array[$i] . "\n"; // with continuous numeric keys
}
Skipping records with foreach
I don't believe that this is a good way to do this (except the case that you have LARGE arrays and slicing it or generating array of keys would use large amount of memory, which 68 is definitively not), but maybe it'll work: :)
$i = 0;
foreach( $array as $key => $item){
if( $i++ < 21){
continue;
}
echo $item . "\n";
}
Using array slice to get sub part or array
Just get piece of array and use it in normal foreach loop.
$sub = array_slice( $array, 21, null, true);
foreach( $sub as $key => $item){
echo $item . "\n";
}
Using next()
If you could set up internal array pointer to 21 (let's say in previous foreach loop with break inside, $array[21] doesn't work, I've checked :P) you could do this (won't work if data in array === false):
while( ($row = next( $array)) !== false){
echo $row;
}
btw: I like hakre's answer most.
Using ArrayIterator
Probably studying documentation is the best comment for this one.
// Initialize array iterator
$obj = new ArrayIterator( $array);
$obj->seek(21); // Set to right position
while( $obj->valid()){ // Whether we do have valid offset right now
echo $obj->current() . "\n";
$obj->next(); // Switch to next object
}
$i = 0;
foreach ($query)
{
if ($i++ < 20) continue;
/* php code to execute if record 21+ */
}
if want to skipped some index then make an array with skipped index and check by in_array function inside the foreach loop if match then it will be skip.
Example:
//you have an array like that
$data = array(
'1' => 'Hello world',
'2' => 'Hello world2',
'3' => 'Hello world3',
'4' => 'Hello world4',
'5' => 'Hello world5',// you want to skip this
'6' => 'Hello world6',// you want to skip this
'7' => 'Hello world7',
'8' => 'Hello world8',
'9' => 'Hello world8',
'10' => 'Hello world8',//you want to skip this
);
//Ok Now wi make an array which contain the index wich have to skipped
$skipped = array('5', '6', '10');
foreach($data as $key => $value){
if(in_array($key, $skipped)){
continue;
}
//do your stuf
}
You have not told what "records" actually is, so as I don't know, I assume there is a RecordIterator available (if not, it is likely that there is some other fitting iterator available):
$recordsIterator = new RecordIterator($records);
$limited = new LimitIterator($recordsIterator, 20);
foreach($limited as $record)
{
...
}
The answer here is to use foreach with a LimitIterator.
See as well: How to start a foreach loop at a specific index in PHP
I'm not sure why you would be using a foreach for this goal, and without your code it's hard to say whether this is the best approach. But, assuming there is a good reason to use it, here's the smallest version I can think of off the top of my head:
$count = 0;
foreach( $someArray as $index => $value ){
if( $count++ < 20 ){
continue;
}
// rest of foreach loop goes here
}
The continue causes the foreach to skip back to the beginning and move on to the next element in the array. It's extremely useful for disregarding parts of an array which you don't want to be processed in a foreach loop.
for($i = 20; $i <= 68; $i++){
//do stuff
}
This is better than a foreach loop because it only loops over the elements you want.
Ask if you have any questions
array.forEach(function(element,index){
if(index >= 21){
//Do Something
}
});
Element would be the current value of index.
Index increases with each turn through the loop.
IE 0,1,2,3,4,5;
array[index];

How to Remove Array Element and Then Re-Index Array?

I have some troubles with an array. I have one array that I want to modify like below. I want to remove element (elements) of it by index and then re-index array. Is it possible?
$foo = array(
'whatever', // [0]
'foo', // [1]
'bar' // [2]
);
$foo2 = array(
'foo', // [0], before [1]
'bar' // [1], before [2]
);
unset($foo[0]); // remove item at index 0
$foo2 = array_values($foo); // 'reindex' array
array_splice($array, 0, 1);
http://php.net/manual/en/function.array-splice.php
You better use array_shift(). That will return the first element of the array, remove it from the array and re-index the array. All in one efficient method.
array_splice($array, array_search(array_value, $array), 1);
2020 Benchmark in PHP 7.4
For these who are not satisfied with current answers, I did a little benchmark script, anyone can run from CLI.
We are going to compare two solutions:
unset() with array_values() VS array_splice().
<?php
echo 'php v' . phpversion() . "\n";
$itemsOne = [];
$itemsTwo = [];
// populate items array with 100k random strings
for ($i = 0; $i < 100000; $i++) {
$itemsOne[] = $itemsTwo[] = sha1(uniqid(true));
}
$start = microtime(true);
for ($i = 0; $i < 10000; $i++) {
unset($itemsOne[$i]);
$itemsOne = array_values($itemsOne);
}
$end = microtime(true);
echo 'unset & array_values: ' . ($end - $start) . 's' . "\n";
$start = microtime(true);
for ($i = 0; $i < 10000; $i++) {
array_splice($itemsTwo, $i, 1);
}
$end = microtime(true);
echo 'array_splice: ' . ($end - $start) . 's' . "\n";
As you can see the idea is simple:
Create two arrays both with the same 100k items (randomly generated strings)
Remove 10k first items from first array using unset() and array_values() to reindex
Remove 10k first items from second array using array_splice()
Measure time for both methods
Output of the script above on my Dell Latitude i7-6600U 2.60GHz x 4 and 15.5GiB RAM:
php v7.4.8
unset & array_values: 29.089932918549s
array_splice: 17.94264793396s
Verdict: array_splice is almost twice more performant than unset and array_values.
So: array_splice is the winner!
Unset($array[0]);
Sort($array);
I don't know why this is being downvoted, but if anyone has bothered to try it, you will notice that it works.
Using sort on an array reassigns the keys of the the array. The only drawback is it sorts the values.
Since the keys will obviously be reassigned, even with array_values, it does not matter is the values are being sorted or not.
Try with:
$foo2 = array_slice($foo, 1);
After some time I will just copy all array elements (excluding these unwanted) to new array

Replace non-specified array values with 0

I want to replace all array values with 0 except work and home.
Input:
$array = ['work', 'homework', 'home', 'sky', 'door']
My coding attempt:
$a = str_replace("work", "0", $array);
Expected output:
['work', 0, 'home', 0, 0]
Also my input data is coming from a user submission and the amount of array elements may be very large.
A bit more elegant and shorter solution.
$aArray = array('work','home','sky','door');
foreach($aArray as &$sValue)
{
if ( $sValue!='work' && $sValue!='home' ) $sValue=0;
}
The & operator is a pointer to the particular original string in the array. (instead of a copy of that string)
You can that way assign a new value to the string in the array. The only thing you may not do is anything that may disturb the order in the array, like unset() or key manipulation.
The resulting array of the example above will be
$aArray = array('work','home', 0, 0)
A loop will perform a series of actions many times. So, for each element in your array, you would check if it is equal to the one you want to change and if it is, change it. Also be sure to put quote marks around your strings
//Setup the array of string
$asting = array('work','home','sky','door')
/**
Loop over the array of strings with a counter $i,
Continue doing this until it hits the last element in the array
which will be at count($asting)
*/
for($i = 0; $i < count($asting);$i++){
//Check if the value at the 'ith' element in the array is the one you want to change
//if it is, set the ith element to 0
if ($asting[$i] == 'work' || $asting[$i] == 'home')
$asting[$i] = 0;
}
Here is some suggested reading:
http://www.php.net/manual/en/language.types.array.php
http://www.php.net/manual/en/language.control-structures.php
But if you are struggling on stuff such as looping, you may want to read some introductory programming material. Which should help you really understand what's going on.
A bit other and much quicker way, but true, need a loop:
//Setup the array of string
$asting = array('bar', 'market', 'work', 'home', 'sky', 'door');
//Setup the array of replacings
$replace = array('home', 'work');
//Loop them through str_replace() replacing with 0 or any other value...
foreach ($replace as $val) $asting = str_replace($val, 0, $asting);
//See what results brings:
print_r ($asting);
Will output:
Array
(
[0] => bar
[1] => market
[2] => 0
[3] => 0
[4] => sky
[5] => door
)
An alternative using array_map:
$original = array('work','home','sky','door');
$mapped = array_map(function($i){
$exclude = array('work','home');
return in_array($i, $exclude) ? 0 : $i;
}, $original);
you may try array_walk function:
function zeros(&$value)
{
if ($value != 'home' && $value != 'work'){$value = 0;}
}
$asting = array('work','home','sky','door','march');
array_walk($asting, 'zeros');
print_r($asting);
You can also give array as a parameter 1 and 2 on str_replace...
Just a small point to the for loop. Many dont realize the second comparing task is done every new iteration. So if it was a case of big array or calculation you could optimize loop a bit by doing:
for ($i = 0, $c = count($asting); $i < $c; $i++) {...}
You may also want to see http://php.net/manual/en/function.array-replace.php for original problem unless the code really is final :)
Try This
$your_array = array('work','home','sky','door');
$rep = array('home', 'work');
foreach($rep as $key=>$val){
$key = array_search($val, $your_array);
$your_array[$key] = 0;
}
print_r($your_array);
There are a few techniques on this page that make zero iterated function calls -- which is good performance-wise. For best maintainability, I recommend separating your list of targeted string as a lookup array. By modifying the original array values by reference, you can swiftly replace whole strings and null coalesce non-targeted values to 0.
Code: (Demo)
$array = ['work', 'homework', 'home', 'sky', 'door'];
$keep = ['work', 'home'];
$lookup = array_combine($keep, $keep);
foreach ($array as &$v) {
$v = $lookup[$v] ?? 0;
}
var_export($array);
Output:
array (
0 => 'work',
1 => 0,
2 => 'home',
3 => 0,
4 => 0,
)
You can very easily, cleanly extend your list of targeted strings by merely extending $keep.
If you don't want a classic loop, you can use the same technique without modifying the original array. (Demo)
var_export(
array_map(fn($v) => $lookup[$v] ?? 0, $array)
);
this my final code
//Setup the array of string
$asting = array('work','home','sky','door','march');
/**
Loop over the array of strings with a counter $i,
Continue doing this until it hits the last element in the array
which will be at count($asting)
*/
for($i = 0; $i < count($asting); $i++) {
//Check if the value at the 'ith' element in the array is the one you want to change
//if it is, set the ith element to 0
if ($asting[$i] == 'work') {
$asting[$i] = 20;
} elseif($asting[$i] == 'home'){
$asting[$i] = 30;
}else{
$asting[$i] = 0;
}
echo $asting[$i]."<br><br>";
$total += $asting[$i];
}
echo $total;

Categories