Is there a more declarative / less horrible way of writing this (deliberately simplified) code?
I realise I can use ->each(), but that still doesn't get rid of the nesting in this case?
Note I do need to generate all the combinations, and I have a mixture of things to loop through - configuration data, ordinary arrays, Eloquent, though obviously I could convert them first...
foreach(config('app.goals') as $goal) {
foreach(config('app.age_groups') as $ages) {
foreach($regions as $region_name => $region_id) {
foreach($interests->pluck('name')->prepend('') as $interest) {
foreach(config('app.devices') as $device) {
$record = new Foo();
$record->goal = $goal;
$record->age = $age;
$record->region = $region_id;
$record->interest = $interest;
$record->device = $device;
$record->save();
}
}
}
}
}
Unclear if there's a Collection method that can help? e.g.
holygrail(config('app.goals'),config('app.age_groups'),$regions...)->each(function($combination) {
// logic for every combination
});
crossJoin() seems to do what I need, it creates an array matrix of every combination.
$matrix = collect($goals)->crossJoin(
$ages,
$regions,
$interests,
$devices
);
So you have an array full of elements, each of them a permutation, e.g:
array(5) {
[0]=>
string(5) "REACH"
[1]=>
string(5) "18-65"
[2]=>
string(9) "Worldwide"
[3]=>
string(11) "Programming"
[4]=>
string(7) "desktop"
}
The Laravel source is in: \Illuminate\Support\Arr::crossJoin
Related
Let's say I have an array (it really could have any depth):
$arr = array(
"lvl1" => array(
"lvl2 => ...
)
)
Now in a function I need to access it like this:
$path = array("lvl1", "lvl2", ...); // array of ordered indexes
$tmp = $arr;
foreach($path as $lvl){
...// other read-only/copy stuff happening on the array, no editing
$tmp = $tmp[$lvl];
}
At this point, just out of curiosity (no real optimization here), am I making copies of copies everytime? Or is it just using references automatically?
TL;DR If you are using PHP 7 the array won't be copied internally unless you change it. This is called copy-on-write.
To understand how PHP works under the hood you can read Reference Counting Basics:
A PHP variable is stored in a container called a "zval".
PHP is smart enough not to copy the actual variable container when it is not necessary.
Let's try to illustrate this on your simplified example using debug_zval_dump:
$array = [
'lvl1' => [
'lvl2' => [
'lvl3' => [
],
],
],
];
$path = ['lvl1', 'lvl2', 'lvl3'];
$tmp = $array;
foreach ($path as $lvl) {
debug_zval_dump($array);
$tmp = $tmp[$lvl];
}
debug_zval_dump($array);
If you run this code you will get the following output:
array(1) refcount(4){
["lvl1"]=>
array(1) refcount(1){
["lvl2"]=>
array(1) refcount(1){
["lvl3"]=>
array(0) refcount(1){
}
}
}
}
array(1) refcount(3){
["lvl1"]=>
array(1) refcount(2){
["lvl2"]=>
array(1) refcount(1){
["lvl3"]=>
array(0) refcount(1){
}
}
}
}
array(1) refcount(3){
["lvl1"]=>
array(1) refcount(1){
["lvl2"]=>
array(1) refcount(2){
["lvl3"]=>
array(0) refcount(1){
}
}
}
}
array(1) refcount(3){
["lvl1"]=>
array(1) refcount(1){
["lvl2"]=>
array(1) refcount(1){
["lvl3"]=>
array(0) refcount(2){
}
}
}
}
Pay attention to refcount: it changes, so internally PHP assigns by reference until you actually change the assigned value. You can read about this in the blog post by nikic:
The important difference to PHP 5 is that all variables were able to share the same array, even though some were PHP references and some weren’t. Only once some kind of modification is performed the array will be separated.
I have the following code but I am wondering how I can make it more efficient.
if ($genres){
$arr = array();
foreach ($genres as $i) {
$arr[] = $i->name;
}
$genres_arr = $arr;
}
if ($themes){
$arr = array();
foreach ($themes as $i) {
$arr[] = $i->name;
}
$themes_arr = $arr;
}
var_dump($genres_arr);
var_dump($themes_arr);
I've tried putting them into an if statement but because they both always exists only the first one runs. I want to check to see if both exist and always run them both through a foreach loop. If only one exists I want only the one to run.
These are the array structures.
["genres"]=>
array(1) {
[0]=>
object(stdClass)#1579 (2) {
["id"]=>
int(25)
["name"]=>
string(26) "Hack and slash/Beat 'em up"
}
}
["themes"]=>
array(3) {
[0]=>
object(stdClass)#1576 (2) {
["id"]=>
int(1)
["name"]=>
string(6) "Action"
}
}
I want to have them as flattered as at the moment they are inside objects. I am then going to implode them into a list for WordPress use.
This code works but its repetitive and some help would be great!
I think you can use array column because it can read values from "A multi-dimensional array or an array of objects from which to pull a column of values from" like this:
if ($genres) {
$genres_arr = array_column($genres, 'name');
}
if ($themes) {
$themes_arr = array_column($themes, 'name');
}
var_dump($genres_arr);
var_dump($themes_arr);
In this way the easiest simplification would be to introduce a new function, which builds the array.
function getNames($arr) {
if (!is_array($arra)) return false;
return array_map(function($item) {
return $item->name;
}, $arr);
}
$themes_arr = getNames($themes);
$genre_arr = getNames($genres);
This is how I would combine them
$sets = [];
if ($genres){
$sets['genres'] = $genres;
}
if ($themes){
$sets['themes'] = $themes;
}
$arr = array();
foreach( $sets as $type => $data ){
foreach ($genres as $i) {
$arr[$type][] = $i->name;
}
}
claudio's answer is perfect for this situation, but in the more general case you can also define a mapping function, which you then use with array_map for both sets of objects, e.g.
$mapper = function ($item) { return $item->name; };
$genres_arr = array_map($mapper, $genres);
$themes_arr = array_map($mapper, $themes);
This has the advantage of being able to run more complex logic (using a getter function instead of direct property access, etc), if you need it in future.
Ideally, both objects would implement a common interface, so that it was clear exactly what kinds of objects the mapping function was designed for.
This is the php code.
<?php
// connect to mongodb
$m = new MongoClient();
// select a database
$db = $m->Example;
$collection="User";
$Query = array("Username"=>$username);
$j = $db->$collection->find($Query);
foreach ($j as $k) {
echo"<pre>";var_dump($k); echo"</pre>";
}
foreach($j as $k => $v) {
echo $k.'='.$j[$k].'<br>';
}
?>
In this, the data is retrieved in $j variable an when var_dump($k) is used the output is as follows:
array(8) {
["_id"]=>
object(MongoId)#6 (1) {
["$id"]=>
string(24) "56d1cb49097ed3241d000029"
}
["Fname"]=>
string(4) "Ritu"
["Lname"]=>
string(3) "Rad"
["Username"]=>
string(4) "riri"
["Password"]=>
string(4) "riri"
["Email"]=>
string(23) "ritikatra#gmail.com"
}
But if you try to display individual key value pair as in the next foreach loop you get the following error:
Fatal error: Cannot use object of type MongoCursor as array
How to display only a particular key and it's value?
eg: Email ritikatra#gmail.com
Result of \MongoCollection::find() (your $j) variable is an instance of \MongoCursor class which implements \Iterator - it allows you to loop over it but it doesn't have keys (i.e. doesn't implement \ArrayAccess). If you want to use your results as an array you should call
$array = iterator_to_array($j);
Now you can use $array as it'd be plain array:
echo $array[0]['Email']
i am trying to get this working: It is ment to return any function or method where the different 'functional parts' are subgrouped. I have problems with the maybe nested {..{.{} } } function body. At the moment the search breaks of after the first following }.
I do have a little bit a hard time understanding lock arround and recursive handling of Regs. So if someone would like to give a brief overall view - would be great!
i understand enough for this..The Regex:
to work with preg_* in PHP with "/im" set
([\w ]+)?(function)+([\s]+[\w\d]+)([\s]*?\(([^\)]+)?\))([\s]?\{+[^\}]*\})
Here a sample of use (i dont have named subpatterns on this sample...):
<?php
$s=file_get_contents('sometestcode');
$p='/
([\w ]+)?(function)+([\s]+[\w\d]+)([\s]*?\(([^\)]+)?\))([\s]?\{+[^\}]*\})
/im';
$m=array();
preg_match_all($p,$s,$m,PREG_SET_ORDER);
var_dump($m);
?>
Here comes the output as expected:
// Here the subject:
public static private function __set($name,$val){
$this->DATA[$name] = $val;
foreach( /*do it*/ ){
//work it...
}
return $this;
}
// The return
array(6){
[1]=>
string(7) "public static private " //makes sence, ha?
[2]=>
string(8) "function"
[3]=>
string(6) " __set" // func/meth name
[4]=>
string(12) "($name,$val)"
[5]=>
string(10) "$name,$val" // (if passed)$argument...
[6]=>
string(60) "{
$this->DATA[$name] = $val;
foreach( /*do it*/ ){
//work it...
}
// Here is the my link
// How to style the regExpPress??
}"
I am working with a series of forms that have subforms embedded into them and I am trying to work out if I can make getValues return the values without the array notation on the subform.
ie:
$form = new Zend_Form();
$subForm = new Zend_Form_SubForm();
$form->addSubForm( $subForm, 'contact' );
$form->addElement(new Zend_Form_Element_Text('name'));
$subForm->addElement( new Zend_Form_Element_Text('phone') );
var_dump($form->getValues());
Gives me the output:
array(2) {
["name"]=>
NULL
["contact"]=>
array(1) {
["phone"]=>
NULL
}
}
But I would actually like the output to be:
array(2) {
["name"]=>
NULL
["phone"]=>
NULL
}
Any easy way of doing this without overriding Zend_Form functions?
You can do it quite simply by using:
$subform->setIsArray(false);
Something like this may be a start:
$data = array();
foreach ($form->getSubForms() as $subform) {
$data += $subform->getValues();
}