I have a multidimensional array, e.g.:
$values = array(
'one' => array(
'title' => 'Title One',
'uri' => 'http://example.com/one',
),
'two' => array(
'title' => 'Title Two',
'uri' => 'http://example.com/two',
),
);
...and I'd like to parse through that with a closure in my implode function, à la:
$final_string = implode(' | ', function($values) {
$return = array();
foreach($values as $value)
$return[] = '' . $value['title'] . '';
return $return;
});
However, this usage yields an Invalid arguments passed error. Is there syntax that I'm missing which will make this use of closures possible? I'm using PHP v5.3.16.
Use array_map:
$final_string = implode(' | ', array_map(function($item) {
return '' . $item['title'] . '';
}, $values));
I trust you'll properly escape the values as HTML in your real code.
As to why this works and your code doesn't, you were passing a function as the second argument to implode. Frankly, that makes little sense: you can join a bunch of strings together, or maybe even a bunch of functions, but you can't join a single function together. It sounds strange, especially if you word it that way.
Instead, we first want to transform all of the items in an array using a function and pass the result of that into implode. This operation is most commonly called map. Luckily, PHP provides this function as, well, array_map. After we've transformed the items in the array, we can join the results.
It seems that you need to assign the function to a variable, and then pass it through to make it work.
$fn = function($values) {
$return = array();
foreach($values as $value)
$return[] = '' . $value['title'] . '';
return $return;
};
$final_string(' | ', $fn($values));
echo $final_string;
I am not sure what the reason is, though, and will need to check it in a little more depth to be able to give you a proper reason.
You can see the code working here
EDIT : Converted this answer to a community wiki so that everyone can contribute here.
EDIT : Explanation by #kmfk
When you pass the closure directly to the implode method - which explicitly wants a second argument of type array, it essentially checks the instanceof - hence the invalid argument. The implode function does not expect mixed type and doesn't know to execute the closure to get an array.
When you first assign that function to a variable, it causes PHP to first evaluate that variable and it ends up passing the returned value from the function into implode.
In that case you're returning an array from the function and passing that into implode - that checks out.
That anonymous function would be instanceof Closure, and
Closure !== array
Ashwin's answer is correct. Here's why:
When you pass the closure directly to the implode method - which explicitly wants a second argument of type array, it essentially checks the instanceof - hence the invalid argument. The implode function does not expect mixed and doesn't know to execute the closure.
When you first assign that function to a variable, it causes PHP to first evaluate that variable and it ends up passing the returned value from the function into implode.
In that case you're returning an array from the function and passing that into implode - that checks out.
edit/adding: that anonymous function would be instanceof Closure.
Closure !== array
You can't use implode to what your trying to achieve, because implode only accept an array as the second argument.
You can try something like this.
$values = array(
'one' => array(
'title' => 'Title One',
'uri' => 'http://example.com/one',
),
'two' => array(
'title' => 'Title Two',
'uri' => 'http://example.com/two',
),
);
$links = array();
foreach ($values as $value) {
$links[] = "<a href='" . $value['uri'] . "'>" . $value['title'] . "</a>";
}
$string = implode(" | ", $links);
function implode_callback( $array, $separator = '', $callback = false )
{
return implode(
$separator,
$callback === false ?
$array : array_map( $callback, $array )
);
}
Example use:
$tab = array( 1, 2, 3 );
echo implode_callback( $tab, '<hr>', function($x) { return "<p>$x</p>"; } );
See example code
Related
I have such code
<?php
$array =
[
['key_name' => 'Amazon Inc.', 'key_code' => '102923'],
['key_name' => 'Google inc, co io', 'key_code' =>'283923'],
['key_name' => 'IMSFT dcs', 'key_code' => '3384823'],
['key_name' => 'MSFTSurfshark io', 'key_code' =>'4473873']
];
$search_text = 'mSfT';
$lowText = strtolower($search_text); // to lower
$lowArr = array_walk_recursive($array, function (&$v) {$v = mb_strtolower(trim($v));}); // need convert to lower case whole array
print_r($lowArr); // here is empty
$r = array_filter($lowArr, function($el) use ($lowText) {return ( strpos($el['key_name'], $lowText) !== false );
});
$keys = array_keys($r); // all keys founded
print_r( $r[$keys[0]]['key_code']); //return only first match
?>
When i want convert array to lower case
$lowArr = array_walk_recursive($array, function ($v) {$v = mb_strtolower(trim($v));});
then script return nothing using https://www.w3schools.com/php/phptryit.asp?filename=tryphp_intro
Goals is to find arrays elemnt by it key_name and return this element key_code
Why this happens?
If i use $array = array_column($array, 'key_name', 'key_code'); how should look like php syntax for same things?
Note this note in the manual.
If callback needs to be working with the actual values of the array, specify the first parameter of callback as a reference. Then, any changes made to those elements will be made in the original array itself.
So you need declare the parameter as a reference in order to make changes to the original array.
function (&$v) {$v = mb_strtolower(trim($v));}
I have an array in PHP defined as:
$myArray = array(
"key_1" => "value_1",
"key_2"=> value_2",
"key_3"=> value_1",
)
Is there a function that could print it as:
[{key_1=value_1, key_2=value_2, key_3=value_3}] ?
I know Java provides Arrays.asList(myHashMap) for doing this for hashmaps (which are associative arrays like PHP)
Is there a function in PHP that's equivalent to Arrays.asList(val) in Java?
I don't know any array method that does this.
One way:
foreach($myArray as $key => $value) {
echo "$key=$value ";
}
Another one (each is deprecated as of php 7.2):
print_r(each($myArray));
Another dirty way you could achieve this is with the following
$array = array(
"key_1" => "value_1",
"key_2" => "value_2",
"key_3" => "value_1",
);
echo str_replace('"', '', str_replace(':', '=', json_encode([$array])));
if you want to print the content of an array with the keys, you can use the print_r($array) or $return = print_r($array,1), to have the string assigned to $return variable.
I want to do something like this:
$product = $productdata['productinfo']['product']; //canvas
$availableSizes = $productdata["productinfo"]["sizes"]; // 8x10 and 10x10
$material = array("matte","luster");
I want to merge combinations of all values so that the result would be 4 string values in a flat array:
Array
(
[0] => canvas_8x10_matte
[1] => canvas_8x10_luster
[2] => canvas_10x10_matte
[3] => canvas_10x10_luster
)
I tried something like this:
$result = array_map(
function ($x, $y) {
return $product . "_" . $x . '_' . $y;
},
$availableSizes,
$material
);
but it only brings me two values:
Array
(
[0] => _8x10_matte
[1] => _12x12_luster
)
And the $product variable seems to be unavailable. How to make it available in the array_map() scope?
array_map() accepts a callable as the first parameter, so $product will not be available in whatever is called due to variable scope. You are going to want to let your anonymous function know to use the global version of $product.
$result = array_map(function ($x, $y) { global $product; return $product . "_" . $x . '_' . $y; }, $availableSizes, $material);
This way the parser knows to look outside the function to find the data you are asking it for.
An even better way is to use inheritance to allow the function to inherit the variables scope from its parent.
$result = array_map(function ($x, $y) use ($product) { return $product . "_" . $x . '_' . $y; }, $availableSizes, $material);
Got tunnel vision on the second part of the question, that I missed the first part. You're only getting the 2 instead of the 4 you are looking for, because array_map passes the array elements in parallel.
Usually when using two or more arrays, they should be of equal length because the callback function is applied in parallel to the corresponding elements.
Now if both arrays are only ever 2 elements in length, you could run a second array_map with the arrays called in reverse order, also reversing $x and $y in your return to keep text order. Then merge both results into one array. But that will break as soon as either array grows beyond 2 elements, and is just messy to maintain.
Try this. Lets loop over your first array, and run the array_map on just the second array, merging the results. Replace your entire $result = array_map(); line with this block.
$result = [];
foreach ($availableSizes as $v) {
$result = array_merge($result, array_map(function ($x) use ($product, $v) {return $product . '_' . $v . '_' . $x;}, $material) );
}
For a fully functional-style approach, nest an array_map() call for each array to be included. On the innermost array_map() join the strings with an underscore, then as that value is passed back up the function scopes, flatten the data with array_merge(...$array) syntax.
The below will continue to work properly regardless of the amount of data in the 3 declared input arrays (of course if any of the array have zero elements, the output will logically be empty). To extend the script to operate with four arrays, another set of array_map() logic will need to be nested. (Demo with 4 arrays)
Code: (Demo)
$products = ['canvas'];
$availableSizes = ['8x10', '10x10'];
$materials = ["matte", "luster"];
var_export(
array_merge(...array_map(
fn($product) => array_merge(...array_map(
fn($size) => array_map(
fn($material) => implode('_', [$product, $size, $material]),
$materials
),
$availableSizes
)),
$products
))
);
I don't know how to make this working. I want to make arrays from URL:
index.php?address=someaddress&telephone=12345&customer=Customer Name&p_name[0]=ProductOne&p_name[1]=ProductTwo&p_price[0]=1&p_price[1]=10&p_name[2]...
There is an api, which is working like this:
$api‐>addItem(array(
'name' => 'ProductOne',
'price' => '123',
));
$api‐>addItem(array(
'name' => 'ProductTwo',
'price' => '32',
));
Is there any way to make arrays like this (=api request $api->addItem(array) from the URL? $api‐>addItem(array can be used multiple times.
EDIT: Thanks dqhendricks and Rocket for pointing out that you can use parse_str() to do the same thing.
$q = parse_str(parse_url($url, PHP_URL_QUERY));
Or you could use this (the long way):
function parse_query($var) {
$var = parse_url($var, PHP_URL_QUERY);
$var = html_entity_decode($var);
$var = explode('&', $var);
$arr = array();
foreach($var as $val) {
$x = explode('=', $val);
$arr[$x[0]] = $x[1];
}
unset($val, $x, $var);
return $arr;
}
Use like this:
$url = "http://someurl.com/click?z=26&a=761";
print_r(parse_query($url));
Do you have control over the URL? If so, I'd change how you send your values.
Instead of:
name1=ProductOne&price1=123&name2=ProductTwo&price2=32
I'd use:
name[]=ProductOne&price[]=123&name[]=ProductTwo&price[]=32
The [] turn them into arrays, meaning $_GET['name'] is now an array. then you can foreach over it.
foreach($_GET['name'] as $k=>$v){
$api->addItem(array(
'name' => $v,
'price' => $_GET['price'][$k]
));
}
// extract the query from the url string
$url = parse_url('sample.php?name1=ProductOne&price1=123&name2=ProductTwo&price2=32', PHP_URL_QUERY);
// process into array so that first element is a key, and second element is a value
parse_str($url, $output_array);
// now $output_array contains the query's variables.
The bigger question is why would you want to do this when these variables are already contained in $_GET?
sample.php?name[]=Product1&name[]=Product2
Then in your PHP, you can see:
print_r($_GET['name']);
I'm not sure if I understood you right, but if what you're looking for is converting a querystring to an array you can user the pares_str function. in order to get only the querystring from a url you can use the parse_url function
$url = "sample.php?some_var=someval&s=4&bla=bla";
$url_parts = parse_url($url);
$qs = $url_parts["query"];
$params = array();
parse_str($qs,$params);
var_dump($params);
Like dqhendricks suggested, would it not pay to just use the raw gets and then append them to an array. I am assuming that you will always know what will be in the URL
$array_of_gets = array("address" => $_GET['address'],"telephone" => $_GET['telephone']);
and so on...
I have a template system, that replaces text such as {HEADER} with the appropriate content. I use an array like this, that replaces the key with the value using str_replace.
$array = array("HEADER","This is the header");
foreach($array as $var => $content) {
$template = str_replace("{" . strtoupper($var). "}", $content,$template);
}
Now im trying to use a defined variable like this:
define("NAME","Site Name");
Inside the value for the header. So I want the defined variable to be inside the array which gets replaced so it would look like this, but it doesn't work.
$array = array("HEADER","Welcome to ".NAME."'s website!");
Any ideas? tell me if im not clear
Shouldn't your array line be:
$array = array("HEADER" => "Welcome to ".NAME."'s website!");
Since you are accessing the array items by key and value?
The way you are looping through the array, using :
foreach($array as $var => $content) {
// ...
}
makes me think you should use an associative array for your $array.
i.e., it should be declared this way :
$array = array("HEADER" => "Welcome to ".NAME."'s website!");
And, as a demonstration, here's an example of code :
$template = <<<TPL
Hello, World !
{HEADER}
And here's the content ;-)
TPL;
define("NAME","Site Name");
$array = array("HEADER" => "Welcome to ".NAME."'s website!");
foreach($array as $var => $content) {
$template = str_replace("{" . strtoupper($var). "}", $content,$template);
}
var_dump($template);
And the output I get it :
string 'Hello, World !
Welcome to Site Name's website!
And here's the content ;-)' (length=73)
Which indicates it's working ;-)
(It wasn't, when $array was declared the way you set it)
When using this :
$array = array("HEADER","This is the header");
var_dump($array);
You are declaring an array that contains two items :
array
0 => string 'HEADER' (length=6)
1 => string 'This is the header' (length=18)
On the other hand, when using this :
$array = array("HEADER" => "This is the header");
var_dump($array);
You are declaring an array that :
contains only one item
"HEADER" being the key
and "This is the header" being the value
Which gives :
array
'HEADER' => string 'This is the header' (length=18)
When using foreach ($array as $key => $value), you need the second one ;-)
And, purely for reference, you can take a look at, in the manual, the following pages :
Arrays
foreach