Altering an Array with Array Walk - php

I have an array that I want to alter. I thought that array_walk would be the way to go but I'm having trouble figuring out how to collect that data from array_walk, deleting the data from the old array, and inserting the new data. How should I go about doing this? Here is the code.
$width_array = array(
"2.5%",
"2.6%",
"2.7%",
"2.8%",
);
function adjust_the_width($value)
{
$value = $value * 2;
}
array_walk($width_array, "adjust_the_width");
$random_width = array_rand($width_array, 10);

You where likely looking for array_map, example below:
<?
$width_array = array(
"2.5%",
"2.6%",
"2.7%",
"2.8%",
);
function adjust_the_width($value) {
return $value * 2;
}
$width_array = array_map("adjust_the_width", $width_array);
$random_width = array_rand($width_array, count($width_array));
var_dump($width_array);
Note: the percentages are dropped from the calculations because PHP interprets the string "2.5%" as a float value when it is multiplied by 2.
Also, array_map supplies each element as the parameter to the function provided and uses it's return value to fill the same place in the new array that array_map builds.
This is also why I assign $width_array = array_map(..., array_map builds a new array, it does not replace the old one by default.
You can also do this if you'd rather not build the intermediate array:
foreach($width_array as &$width) {
$width = $width * 2;
}
var_dump($width_array);
This walks the array and modifies each element as a reference to it's location (that's what &$width means).
Without the '&' this foreach loop will do nothing but chew cpu cycles.

Related

converting a posted php array to a multidimensional array [duplicate]

So I wrote a class that can parse XML documents and create SQL queries from it to update or insert new rows depending on the settings.
Because the script has to work with any amount of nested blocks, the array that I'm putting all the values in has its path dynamically created much like the following example:
$path = array('field1','field2');
$path = "['".implode("']['",$path)."']";
eval("\$array".$path."['value'] = 'test';");
Basically $path contains an array that shows how deep in the array we currently are, if $path contains for instance the values main_table and field I want set $array['main_table']['field']['value'] to 'test'
As you can see I am currently using eval to do this, and this works fine. I am just wondering if there is a way to do this without using eval.
Something like
$array{$path}['value'] = 'test'; but then something that actually works.
Any suggestions?
EDIT
The reason I'm looking for an alternative is because I think eval is bad practice.
SECOND EDIT
Changed actual code to dummy code because it was causing a lot of misunderstandings.
Use something like this:
/**
* Sets an element of a multidimensional array from an array containing
* the keys for each dimension.
*
* #param array &$array The array to manipulate
* #param array $path An array containing keys for each dimension
* #param mixed $value The value that is assigned to the element
*/
function set_recursive(&$array, $path, $value)
{
$key = array_shift($path);
if (empty($path)) {
$array[$key] = $value;
} else {
if (!isset($array[$key]) || !is_array($array[$key])) {
$array[$key] = array();
}
set_recursive($array[$key], $path, $value);
}
}
You can bypass the whole counter business with the array append operator:
$some_array[] = 1; // pushes '1' onto the end of the array
As for the whole path business, I'm assuming that's basically an oddball representation of an xpath-like route through your xml document... any reason you can't simply use that string as an array key itself?
$this->BLOCKS['/path/to/the/node/you're/working/on][] = array('name' => $name, 'target' => $target);
You can use a foreach with variable variables.
// assuming a base called $array, and the path as in your example:
$path = array('field1','field2');
$$path = $array;
foreach ($path as $v) $$path = $$path[$v];
$$path['value'] = 'test';
Short, simple, and much better than eval.

Is there a faster way to search something in an array than in_array in PHP?

I need to check some input string against a huge (and growing) list of strings coming from a CSV file (1000000+). I currently load every string in an array and check against it via in_array(). Code looks like this:
$filter = array();
$filter = ReadFromCSV();
$input = array("foo","bar" /* more elements... */);
foreach($input as $i){
if(in_array($i,$filter)){
// do stuff
}
}
It already takes some time and I was wondering is there is a faster way to do this?
in_array() checks every element in the array until it finds a match. The average complexity is O(n).
Since you are comparing strings, you might store your input as array keys instead of values and look them up via array_key_exists(); which requires a constant time O(1).
Some code:
$filter = array();
$filter = ReadFromCSV();
$filter = array_flip($filter); // switch key <=> value
$input = array("foo","bar" /* more elements... */);
foreach($input as $i){
if(array_key_exists($i,$filter)){ // array_key_exists();
// do stuff
}
}
That's what indexes were invented for.
It's not a matter of in_array() speed, as the data grows, you should probably consider using indexes by loading data into a real DBMS.

Find index of value in associative array in php?

If you have any array $p that you populated in a loop like so:
$p[] = array( "id"=>$id, "Name"=>$name);
What's the fastest way to search for John in the Name key, and if found, return the $p index? Is there a way other than looping through $p?
I have up to 5000 names to find in $p, and $p can also potentially contain 5000 rows. Currently I loop through $p looking for each name, and if found, parse it (and add it to another array), splice the row out of $p, and break 1, ready to start searching for the next of the 5000 names.
I was wondering if there if a faster way to get the index rather than looping through $p eg an isset type way?
Thanks for taking a look guys.
Okay so as I see this problem, you have unique ids, but the names may not be unique.
You could initialize the array as:
array($id=>$name);
And your searches can be like:
array_search($name,$arr);
This will work very well as native method of finding a needle in a haystack will have a better implementation than your own implementation.
e.g.
$id = 2;
$name= 'Sunny';
$arr = array($id=>$name);
echo array_search($name,$arr);
Echoes 2
The major advantage in this method would be code readability.
If you know that you are going to need to perform many of these types of search within the same request then you can create an index array from them. This will loop through the array once per index you need to create.
$piName = array();
foreach ($p as $k=>$v)
{
$piName[$v['Name']] = $k;
}
If you only need to perform one or two searches per page then consider moving the array into an external database, and creating the index there.
$index = 0;
$search_for = 'John';
$result = array_reduce($p, function($r, $v) use (&$index, $search_for) {
if($v['Name'] == $search_for) {
$r[] = $index;
}
++$index;
return $r;
});
$result will contain all the indices of elements in $p where the element with key Name had the value John. (This of course only works for an array that is indexed numerically beginning with 0 and has no “holes” in the index.)
Edit: Possibly even easier to just use array_filter, but that will not return the indices only, but all array element where Name equals John – but indices will be preserved:
$result2 = array_filter($p, function($elem) {
return $elem["Name"] == "John" ? true : false;
});
var_dump($result2);
What suits your needs better, resp. which one is maybe faster, is for you to figure out.

Split a string to form multidimensional array keys?

I'm creating JSON encoded data from PHP arrays that can be two or three levels deep, that look something like this:
[grandParent] => Array (
[parent] => Array (
[child] => myValue
)
)
The method I have, which is simply to create the nested array manually in the code requires me to use my 'setOption' function (which handles the encoding later) by typing out some horrible nested arrays, however:
$option = setOption("grandParent",array("parent"=>array("child"=>"myValue")));
I wanted to be able to get the same result by using similar notation to javascript in this instance, because I'm going to be setting many options in many pages and the above just isn't very readable, especially when the nested arrays contain multiple keys - whereas being able to do this would make much more sense:
$option = setOption("grandParent.parent.child","myValue");
Can anyone suggest a way to be able to create the multidimensional array by splitting the string on the '.' so that I can json_encode() it into a nested object?
(the setOption function purpose is to collect all of the options together into one large, nested PHP array before encoding them all in one go later, so that's where the solution would go)
EDIT: I realise I could do this in the code:
$options['grandparent']['parent']['child'] = "myValue1";
$options['grandparent']['parent']['child2'] = "myValue2";
$options['grandparent']['parent']['child3'] = "myValue3";
Which may be simpler; but a suggestion would still rock (as i'm using it as part of a wider object, so its $obj->setOption(key,value);
This ought to populate the sub-arrays for you if they haven't already been created and set keys accordingly (codepad here):
function set_opt(&$array_ptr, $key, $value) {
$keys = explode('.', $key);
// extract the last key
$last_key = array_pop($keys);
// walk/build the array to the specified key
while ($arr_key = array_shift($keys)) {
if (!array_key_exists($arr_key, $array_ptr)) {
$array_ptr[$arr_key] = array();
}
$array_ptr = &$array_ptr[$arr_key];
}
// set the final key
$array_ptr[$last_key] = $value;
}
Call it like so:
$opt_array = array();
$key = 'grandParent.parent.child';
set_opt($opt_array, $key, 'foobar');
print_r($opt_array);
In keeping with your edits, you'll probably want to adapt this to use an array within your class...but hopefully this provides a place to start!
What about $option = setOption("grandParent", { parent:{ child:"myValue" } });?
Doing $options['grandparent']['parent']['child'] will produce an error if $options['grandparent']['parent'] was not set before.
The OO version of the accepted answer (http://codepad.org/t7KdNMwV)
$object = new myClass();
$object->setOption("mySetting.mySettingsChild.mySettingsGrandChild","foobar");
echo "<pre>".print_r($object->options,true)."</pre>";
class myClass {
function __construct() {
$this->setOption("grandparent.parent.child","someDefault");
}
function _setOption(&$array_ptr, $key, $value) {
$keys = explode('.', $key);
$last_key = array_pop($keys);
while ($arr_key = array_shift($keys)) {
if (!array_key_exists($arr_key, $array_ptr)) {
$array_ptr[$arr_key] = array();
}
$array_ptr = &$array_ptr[$arr_key];
}
$array_ptr[$last_key] = $value;
}
function setOption($key,$value) {
if (!isset($this->options)) {
$this->options = array();
}
$this->_setOption($this->options, $key, $value);
return true;
}
}
#rjz solution helped me out, tho i needed to create a array from set of keys stored in array but when it came to numerical indexes, it didnt work. For those who need to create a nested array from set of array indexes stores in array as here:
$keys = array(
'variable_data',
'0',
'var_type'
);
You'll find the solution here: Php array from set of keys

Php: what complexity [i.e O(N), O(1), ...] the the square brackets add function has? i.e $x[] = 'value'

Imagine you are adding several elements to an array:
$array[] = 'dog';
$array[] = 'cat';
$array[32] = 'lachupacabra';
$array[] = 'cow';
Do you know what index the 'cow' will have? No? Neither did I until I've found out that it's 33. This seems logical, php should have a reference to the last element added to array, and while adding element with square brackets syntax it takes previous element, takes it's index increments it by one and associates the incremented index with the new value. But stop, this might cause trouble if the incremented index was added earlier. Example:
$array[] = 'dog';
$array[33] = 'cat';
$array[32] = 'lachupacabra';
$array[] = 'cow';
Gues what, the index of 'cow' is 34. Does it mean that php tests all keys before adding another one with the square brackets syntax? This will have O(N) complexity or is it storing something like $max_key value somewhere inside, the operation will have O(1) complexity but will use extra memory?
Question #1: What complexity the '[]' operation has?
Question #2: How the hell it is possible to get the index that the element was added at?
(Regarding Question#2 I'd prefer the O(1) operation if you don't mind)
EDIT
I need to implement the following method:
class X {
private $array;
public function AddItem($item) {
$array[] = 'new value';
return get_highest_index_with_o_1_comlexity($array);
}
public function RemoveItem($index) {
unset($this->array[$index]);
}
}
To get the key of the last element in an array use:
end($array);
$key = key($array);
end positions the cursor on the last element of the array, key returns the key at the current cursor position.
PHP uses the (largest index + 1) as the next index when using []. For example, If you had $arr = array(42 => 'foo') then the next element assigned would have the key 43. Array inserts happen in constant time. It's extremely silly to worry about this.

Categories