I want to remove an element from a PHP array (and shrink the array size). Just looking at the PHP docs, it seems this can be done using array_slice() and array_merge()
so I am guessing (off the top of my head) that some combination of array_merge() and array_slice will work. However, array_slice() requires an index (not a key), so I'm not sure how to quickly cobble these functions together for a solution.
Has anyone implemented such a function before?. I'm sure it must be only a few lines long, but I cant somehow get my head around it (its been one of those days) ...
Actually, I just came up with this cheesy hack when writing up this question....
function remove_from_array(array $in, value) {
return array_diff($in, (array)$value);
}
too ugly? or will it work (without any shocking side effects)?
This functionality already exists; take a look at unset.
http://php.net/manual/en/function.unset.php
$a = array('foo' => 'bar', 'bar' => 'gork');
unset($a['bar']);
print_r($a);
output will be:
array(
[foo] => bar
)
There's the array_filter function that uses a callback function to select only wanted values from the array.
you want an unset by value. loop through the array and unset the key by value.
unset($my_array['element']);
Won't work?
This code can be replaced by single array_filter($arr) call
foreach($array as $key => $value) {
if($value == "" || $value == " " || is_null($value)) {
unset($array[$key]);
}
}
/*
and if you want to create a new array with the keys reordered accordingly...
*/
$new_array = array_values($array);
Related
I have an array containing several keys, values, objects etc.. I need to empty that array but I'd like to do it in the most efficient manner.
The best I can come up with is:
foreach ($array as $key => $val) unset($array[$key]);
But I don't like the idea of having to loop through the array to just empty it.. surely there's a nice slick/clever way of doing this without wasting memory creating a new array?
Note: I'm not sure myself if it does cost extra memory in creating the array as new again. If it doesn't then $array = new array(); would be a fine way of 'emptying' it.
Just try with:
$array = array();
It highly depends on what you mean.
To empty the current reference you can always do
$array = array();
To completely remove the current instance from the scope
unset($array);
Unfortunately both of these cases don't necessarily mean the memory associated with each element is released.
PHP works with something called "references" for your variables. Your variables are actually labels or references pointing to data, not the actual container for data.
The PHP garbage collector can offer more insight on this subject.
Now take a look at this example, taken from the docs:
$a = "new string";
$c = $b = $a;
xdebug_debug_zval( 'a' );# a: (refcount=3, is_ref=0)='new string'
unset( $b, $c );
xdebug_debug_zval( 'a' );# a: (refcount=1, is_ref=0)='new string'
This unfortunately applies to all your variables. Including arrays. Cleaning up the memory associated with the array is a whole different subject I'm afraid.
I've noticed a longer discussion in the comments regarding using unset() on each individual key.
This feels like extremely bad practice. Consider the following code:
class A{
function __construct($name){$this->name=$name;}
function __destruct(){echo $this->name;}
}
$a=array();
$b=array();
$c=array();
for($i=0;$i<5;$i++) {
$a[]=new A('a');
$b[]=new A('b');
$c[]=new A('c');
}
unset($a);
$b=array();
echo PHP_EOL.'done'.PHP_EOL;
This will output:
aaaaabbbbb
done
ccccc
When the reference to a particular data structure reaches 0, it is cleaned from memory.
Both =array() and unset will do the same thing.
Now if you don't actually need array() you can use null :
$array=null;
This keeps the label in memory, but removes the reference it held to any particular data.
It's simple:
$array = array();
$array will be existing and type of array (but empty), and your data can be garbaged later from memory.
Well... why not: $array = array(); ?
As Suresh Kamrushi pointed out, I could use array_keys:
foreach (array_keys($array) as $key) unset($array[$key]);
This is probably the nicest solution for now.. but I'm sure someone will come up with something better soon :)
Try this:
// $array is your original array
$array = array_combine( array_keys( $array ), array_fill( 0, count($array), 0 ) );
The above will blank your array keeping the keys intact.
Hope this helps.
In the following script function clean($data) calls it within it, that I understand but how it is cleaning data in the statement $data[clean($key)] = clean($value);??? Any help is appreciated.. I am trying to figure it out as I am new to PHP. Regards.
if (ini_get('magic_quotes_gpc')) {
function clean($data) {
if (is_array($data)) {
foreach ($data as $key => $value) {
$data[clean($key)] = clean($value);
}
} else {
$data = stripslashes($data);
}
return $data;
}
$_GET = clean($_GET);
$_POST = clean($_POST);
$_REQUEST = clean($_REQUEST);
$_COOKIE = clean($_COOKIE);
}
Your Question:
So if I undertsand correctly you want to know what is the function doing in the line
$data[clean($key)] = clean($value);
The Answer:
See the prime purpose of the function is to remove slashes from string with php's stripslashes method.
If the input item is an array then it tries to clean the keys of the array as well as the values of the array by calling itself on the key and value.
In php arrays are like hashmaps and you can iterate over the key and value both with foreach loop like following
foreach ($data as $key => $value) {....}
So if you want to summarize the algorithm in your code snippet it would be as under
Check if the input is array. If it is not then go to step 4
For each item of array clean the key and value by calling clean method on it (Recursively)
Return the array
clean the input string using stripslashes method
5 return the cleaned input
From my understanding it's not cleaning the key but creates a new element with a clean key while the uncleaned key remains.
$a['foo\bar'] : val\ue
becomes
$a['foo\bar'] : val\ue
$a['foobar'] : value
Someone correct me if im wrong.
Maybe you'll understand the code better if it's put this way:
foreach ($data as $key => $value) {
$key = clean($key); // Clean the key, the
$value = clean($value); // Clean the value
$data[$key] = $value; // Put it in the array that will be returned
}
Assuming you have an array like this:
$_POST = array(0 => 'foo', 1 => array('bar' => 'baz'));
the following will happen:
Call clean($_POST);
call clean 0
call clean 'foo'
$return[0] = 'foo'
call clean 1
call clean 'bar'
call clean 'baz'
$return[1] = array('bar' => 'baz');
You should probably read this: http://www.codewalkers.com/c/a/Miscellaneous/Recursion-in-PHP/
The main purpose of the function is to clean an associative array or a single variable. An associative array is an array where you define keys and values for that keys; so are special arrays used in PHP like $_GET $_POST and so on.
The meaning of "cleaning" is to check whether magic quotes are active - this causes some characters in these arrays to be escaped with backslashes when you post dynamic data to a PHP page.
$_GET["Scarlett"] = "O' Hara" becomes with magic quotes $_GET["Scarlett"] = "O\' Hara"
So if magic quotes are active, the function takes care of this, and slashes are stripped so that the strings retain their correct, not escaped value.
The algorithm checks if the data passed to the function is an array, if not it cleans directly the value.
$string = "Escapes\'in\'a string";
clean($string);
is it an array? No. Then return stripslashes(my data)
$array = array("key\'with\'escapes"=>"value\'with\'escapes", "another\'key"=>"another value");
clean($array)
is it an array? Yes. So cycle through each key/value pair with foreach, take the key and clean it like the first example; then take the value and do the same and put the cleaned versions in the array.
As you see the function has two different behaviours differentiated by that "if" statement.
If you pass an array, you activate the second behaviour that in turns passes couples of strings, not arrays, triggering the first behaviour.
My thought is that this function doesn't work properly, though. Anyone got the same sensation? I have it not tested yet but it seems it's not "cleaning" the key/values in the sense of replacing them, but adds the cleaned versions along the uncleaned ones.
Is there a better way to search through $_SESSION variables (or any array) for a particular string than:
foreach($_SESSION as $k => $v){
if(strstr($k, 'p_')){
Thanks.
edit: My keys will look similar to:
p_123
p_456
i_123
...
If your $_SESSION structure must stay "as is", IMO it is Ok.
However if all 'p_' elements could go under an array index like $_SESSION['p'] = array('key1' => 'val1', ...), you could retrieve all 'p' elements at once.
BTW this is only micro optimization, go with the structure you're fine with.
EDIT: Just be careful with strstr(): if one day you must store keys like i_123_p_456 into your $_SESSION array, you should switch to if (strpos($k, 'p_') === 0).
I'm adding another answer as I'm taking a completely different approach.
If you're going to do this a lot, you could consider using filters.
function pprefix($var) {
return(strstr($var, 'p_'));
}
$filtered = array_filter(array_keys($_SESSION), "pprefix");
foreach($fildered as $k) {
echo("$k => " . $_SESSION[$k]);
}
try this:
if (array_key_exists('p_', $_SESSION)) {
echo($_SESSION['p_']);
}
Or, if you only knows part of the name of the key (like the beggining), I'd use the array_keys() instead $k => $v, like so:
foreach(array_keys($_SESSION) as $k) {
if(strstr($k, 'p_')){
// do something.
}
}
I'm trying to obtain the first key of an associative array, without creating a temporary variable via array_keys() or the like, to pass by reference. Unfortunately both reset() and array_shift() take the array argument by reference, so neither seem to be viable results.
With PHP 5.4 I'll be in heaven; array_keys($array)[0];, but unfortunately this of course is not an option either.
I could create a function to serve the purpose, but I can only imagine there is some concoction of PHP's array_* functions that will produce the desired result in a single statement, that I cannot think of or come up with.
So:
$array = array('foo' => 'bar', 'hello' => 'world');
$firstKey = assorted_functions($array); // $firstKey = 'foo'
The reason for the "no reference" clause in my question is only for the fact that I assume array_keys() will be required (if there is a way passing by reference, please fire away)
I'd use key(), but that requires a reset() as I'm not sure where the pointer will be at the time of this operation.
Addendum
I'm following up on a realization I had recently: as I mentioned in the comments, it'll use the memory all the same, so if that's a concern, this question hath no solution.
$a = range(0,99999);
var_dump(memory_get_peak_usage()); // int(8644416)
$k = array_keys($a)[0];
var_dump(memory_get_peak_usage()); // int(17168824)
I knew this, as PHP doesn't have such optimization capabilities, but figured it warranted explicit mention.
The brevity of the accepted answer is nice though, and'll work if you're working with reasonably sized arrays.
Although array_shift(array_keys($array)); will work, current(array_keys($array)); is faster as it doesn't advance the internal pointer.
Either one will work though.
Update
As #TomcatExodus noted, array_shift(); expects an array passed by reference, so the first example will issue an error. Best to stick with current();
You can use reset and key:
reset( $array );
$first_key = key( $array );
or, you can use a function:
function firstIndex($a) { foreach ($a as $k => $v) return $k; }
$key = firstIndex( $array );
array_shift(array_keys($array))
each() still a temporary required, but potentially a much smaller overhead than using array_keys().
What about using array_slice (in combination with array_keys for associative arrays)?
$a = range(0,999999);
var_dump(memory_get_peak_usage());
$k = array_keys(array_slice($a, 0, 1, TRUE))[0];
var_dump(memory_get_peak_usage());
var_dump($k);
$k = array_keys($a)[0];
var_dump(memory_get_peak_usage());
Gives as output (at least with me):
int(36354360)
int(36355112)
int(0)
int(72006024)
int(0)
This might sounds like a silly question. How do I get the 1st value of an array without knowing in advance if the array is associative or not?
In order to get the 1st element of an array I thought to do this:
function Get1stArrayValue($arr) { return current($arr); }
is it ok?
Could it create issues if array internal pointer was moved before function call?
Is there a better/smarter/fatser way to do it?
Thanks!
A better idea may be to use reset which "rewinds array's internal pointer to the first element and returns the value of the first array element"
Example:
function Get1stArrayValue($arr) { return reset($arr); }
As #therefromhere pointed out in the comment below, this solution is not ideal as it changes the state of the internal pointer. However, I don't think it is much of an issue as other functions such as array_pop also reset it.
The main concern that it couldn't be used when iterating over an array isn't an problem as foreach operates on a copy of the array. The PHP manual states:
Unless the array is referenced, foreach operates on a copy of the specified array and not the array itself.
This can be shown using some simple test code:
$arr = array("a", "b", "c", "d");
foreach ( $arr as $val ){
echo reset($arr) . " - " . $val . "\n";
}
Result:
a - a
a - b
a - c
a - d
To get the first element for any array, you need to reset the pointer first.
http://ca3.php.net/reset
function Get1stArrayValue($arr) {
return reset($arr);
}
If you don't mind losing the first element from the array, you can also use
array_shift() - shifts the first value of the array off and returns it, shortening the array by one element and moving everything down. All numerical array keys will be modified to start counting from zero while literal keys won't be touched.
Or you could wrap the array into an ArrayIterator and use seek:
$array = array("foo" => "apple", "banana", "cherry", "damson", "elderberry");
$iterator = new ArrayIterator($array);
$iterator->seek(0);
echo $iterator->current(); // apple
If this is not an option either, use one of the other suggestions.