Better way to access Laravel array inputs? - arrays

Currently I have an array sort. Sort has only one key / value. The keys and values are always different. This array always has just 1 key/value pair. How do I access both elements dynamically in laravel?
I have already solved this but think it is extremely inefficient.
my current solution
I made a function orderQuery() to return the key name.
function orderQuery() {
foreach (Input::get('sort') as $key => $value) {
return $key; // there is only 1 item in the array but this looks like bad practice
}
}
Then I call it like this to respond to my request
->orderBy(orderQuery(), Input::get('sort.'.orderQuery()))
Is there a better way to do this?

You can use key()
$key = key(Input::get('sort'));
If you want to be save reset the pointer first:
$sort = Input::get('sort');
reset($sort);
$key = key($sort);

Related

Merge Perl hashes into one array and loop through it

I'm creating a Perl plugin for cPanel which has to get all domains in the account of a user and display it in a HTML select field. Originally, I'm a PHP developer, so I'm having a hard time understanding some of the logic of Perl. I do know that cPanel plugins can also be written in PHP, but for this plugin I'm limited to Perl.
This is how I get the data from cPanel:
my #user_domains = $cpliveapi->uapi('DomainInfo', 'list_domains');
#user_domains = $user_domains[0]{cpanelresult}{result}{data};
This is what it looks like using print Dumper #user_domains:
$VAR1 = {
'addon_domains' => ['domain1.com', 'domain2.com', 'domain3.com'],
'parked_domains' => ['parked1.com', 'parked2.com', 'parked3.com'],
'main_domain' => 'main-domain.com',
'sub_domains' => ['sub1.main-domain.com', 'sub2.main-domain.com']
};
I want the data to look like this (thanks #simbabque):
#domains = qw(domain1.com domain2.com domain3.com main-domain.com parked1.com parked2.com parked3.com);
So, I want to exclude sub_domains and merge the others in 1 single-dimensional array so I can loop through them with a single loop. I've struggled the past few days with what sounds like an extremely simple task, but I just can't wrap my head around it.
You need something like this
If you find you have a copy of List::Util that doesn't include uniq then you can either upgrade the module or use this definition
sub uniq {
my %seen;
grep { not $seen{$_}++ } #_;
}
From your dump, the uapi call is returning a reference to a hash. That goes into $cp_response and then drilling down into the structure fetches the data hash reference into $data
delete removes the subdomain information from the hash.
The lists you want are the values of the hash to which $data refers, so I extract those. Those values are references to arrays of strings if there is more than one domain in the list, or simple strings if there is only one
The map converts all the domain names to a single list by dereferencing array references, or passing strings straight through. That is what the ref() ? #$_ : $_ is doing. FInally uniq removes multiple occurrences of the same name
use List::Util 'uniq';
my $cp_response = $cpliveapi->uapi('DomainInfo', 'list_domains');
my $data = $cp_response->{cpanelresult}{result}{data};
delete $data->{sub_domains};
my #domains = uniq map { ref() ? #$_ : $_ } values %$data;
output
parked1.com
parked2.com
parked3.com
domain1.com
domain2.com
domain3.com
main-domain.com
That isn't doing what you think it' doing. {} is the anonymous hash constructor, so you're making a 1 element array, with a hash in it.
You probably want:
use Data::Dumper;
my %user_domains = (
'addon_domains' => ['domain1.com', 'domain2.com', 'domain3.com'],
'parked_domains' => ['parked1.com', 'parked2.com', 'parked3.com'],
'main_domain' => 'main-domain.com',
'sub_domains' => ['sub1.main-domain.com', 'sub2.main-domain.com'],
);
print Dumper \%user_domains;
And at which point the 'other' array elements you can iterate through either a double loop:
foreach my $key ( keys %user_domains ) {
if ( not ref $user_domains{$key} ) {
print $user_domains{$key},"\n";
next;
}
foreach my $domain ( #{$user_domains{$key}} ) {
print $domain,"\n";
}
}
Or if you really want to 'flatten' your hash:
my #flatten = map { ref $_ : #$_ ? $_ } values %user_domains;
print Dumper \#flatten;
(You need the ref test, because without it, the non-array main-domain won't work properly)
So for the sake of consistency, you might be better off with:
my %user_domains = (
'addon_domains' => ['domain1.com', 'domain2.com', 'domain3.com'],
'parked_domains' => ['parked1.com', 'parked2.com', 'parked3.com'],
'main_domain' => ['main-domain.com'],
'sub_domains' => ['sub1.main-domain.com', 'sub2.main-domain.com'],
);

Accessing returned values as an array

I have simple XML that I want to read in Perl and make hash containing all read keys.
Consider this code:
my $content = $xml->XMLin("filesmap.xml")->{Item};
my %files = map { $_->{Path} => 1 } #$content;
This snippet works great when XML file contains many Item tags. Then $content is a reference to array. But when there is only one Item I get an error when dereferencing as array. My assumption is that $content is a reference to the scalar, not array.
What is the practice to make sure I get array of values read from XML?
What you need is to not use XML::Simple and then it's really trivial. My favourite for fairly straightforward XML samples is XML::Twig
use XML::Twig;
my $twig = XML::Twig -> new -> parsefile ( 'filesmap.xml' );
my #files = map { $_ -> trimmed_text } $twig -> get_xpath ( '//Path' );
With a more detailed XML sample (and desired result) I'll be able to give you a better answer.
Part of the problem with XML::Simple is it tries to turn an XML data structure into perl data structures, and - because hashes are key-values and unordered but arrays are ordered, it has to guess. And sometimes it does it wrong, and other times it's inconsistent.
If you want it to be consistent, you can set:
my $xml = XMLin( "filesmap.xml", ForceArray => 1, KeyAttr => [], ForceContent => 1 );
But really - XML::Simple is just a route to pain. Don't use it. If you don't like XML::Twig, try XML::LibXML instead.
What I would say you need is a flatten-ing step.
my %files
= map { $_->{Path} => 1 }
# flatten...
map { ref() eq 'ARRAY' ? #$_ : $_ }
$xml->XMLin("filesmap.xml")->{Item}
;
You can do a check and force the return into an array reference if necessary:
my $content = $xml->XMLin("filesmap.xml")->{Item};
$content = ref $content eq 'ARRAY'
? $content
: [$content];

Accessing array values of a field collection in a node with Drupal?

Please bare with a very recent user of Drupal.
I want to create an array out of all examples of the string "url" on a Drupal site.
I've used the method "field_get_items" previously to do something very similar, but I am now trying to access a field collection that is many levels deep into the node's array and I'm not sure that method would work.
$website_urls = array();
$faculty_members = field_get_items('node', $node, 'field_faculty_member');
for ($i = 0; $i < count($faculty_members); $i++) {
$value = field_view_value('node', $node, 'field_faculty_member', $faculty_members[$i]);
$field_collection = $value['entity']['field_collection_item'][key($value['entity']['field_collection_item'])];
$website_urls[] = render($field_collection['field_link']['#items'][0]['url']);
}
An example of one url's location is...
['field_faculty_program'][0]['entity']['field_collection_item'][1842]['field_faculty_member'][0]['entity']['field_collection_item'][1843]['field_link']['#items'][0]['url']
..and another...
['field_faculty_program'][4]['entity']['field_collection_item'][1854]['field_faculty_member'][0]['entity']['field_collection_item'][1855]['field_link']['#items'][0]['url']
What is the method I should be using to collect al of the 'url' strings for placement in an array?
You can actually still use the field_get_items() function but eventually pass it 'field_collection_item' instead for the node type.
Something like this should work:
if ($items = field_get_items('node', $node, 'field_faculty_member')) {
//loop through to get the ids so we can take
//advantage of field_collection_item_load_multiple for
//greater efficiency
$field_collection_item_ids = array();
foreach ($items as $item) {
$field_collection_item_ids[] = $item['value'];
}
if ($field_collection_items = field_collection_item_load_multiple($field_collection_item_ids)) {
foreach ($field_collection_items as $subitem) {
//now we load the items within the field collection
if ($items = field_get_items('field_collection_item', $subitem, 'field_faculty_member')) {
//And you can then repeat to go deeper and deeper
//e.g. a field collection item within a field collection
//for instance to get the urls within your faculty members
//item. Best to break this into functions or a class
//to keep your code readable and not have so many nested
//if statements and for loops
}
}
}
}
Hope that helps!
Scott

Why do I get the same value from iterating over this hash?

I'm trying to put a hash %listvol into an array #fileInfo in Perl.
#fileInfo = ($filename, $data, $index, \%listvol);
%listvol contains a list of volume: key = $vol, value = $vol.
The first $vol values are ABCDEF, then GFFFF, EEEAA - always different.
Then I put the array #fileInfo in the hash %listeAllFile:
$listeAllFile{$nameOfFile} = [#fileInfo];
Later I'm trying to get the hash %listvol without success. I'm using this code:
foreach $key (keys %listeAllFile) {
#tab = #{ $listeAllFile{$key} };
$filename = $tab[0];
%listvol = %{ $tab[3] };
foreach $vol (keys %listvol) {
print "\n vol is $vol for file $filename";
}
The file name is always different, so it is ok. But the value of the variable $vol is always the same, ABCDEF. It seems that I get get each time the same value.
Does anyone have an idea?
While you didn't include code to reproduce your problem, I'm fairly sure that the issue is that you're storing a reference to the same %listvol hash in each array.
When you change the contents of %listvol for the second entry, you're modifying the first entry at the same time. One way to fix that is to use {%listvol} instead of \%listvol. The former makes a shallow copy of the current contents of %listvol, just like [#fileInfo] makes a shallow copy of the current contents of #fileInfo.

Unsetting elements of a cakephp generated array

I have an array called $all_countries following this structure:
Array
(
[0] => Array
(
[countries] => Array
(
[id] => 1
[countryName] => Afghanistan
)
)
[1] => Array
(
[countries] => Array
(
[id] => 2
[countryName] => Andorra
)
)
)
I want to loop through an array called prohibited_countries and unset the entire [countries] element that has a countryName matching.
foreach($prohibited_countries as $country){
//search the $all_countries array for the prohibited country and remove it...
}
Basically I've tried using an array_search() but I can't get my head around it, and I'm pretty sure I could simplify this array beforehand using Set::extract or something?
I'd be really grateful if someone could suggest the best way of doing this, thanks.
Here's an example using array_filter:
$all_countries = ...
$prohibited_countries = array('USA', 'England'); // As an example
$new_countries = array_filter($all_countries, create_function('$record', 'global $prohibited_countries; return !in_array($record["countries"]["countryName"], $prohibited_countries);'));
$new_countries now contains the filtered array
Well first of all id e teh array in the format:
Array(
'Andorra' => 2,
'Afghanistan' => 1
);
Or if you need to have the named keys then i would do:
Array(
'Andorra' => array('countryName'=> 'Andorra', 'id'=>2),
'Afghanistan' => array('countryName'=> 'Afghanistan', 'id'=>1)
);
then i would jsut use an array_diff_keys:
// assuming the restricted and full list are in the same
// array format as outlined above:
$allowedCountries = array_diff_keys($allCountries, $restrictedCountries);
If your restricted countries are just an array of names or ids then you can use array_flip, array_keys, and/or array_fill as necessary to get the values to be the keys for the array_diff_keys operation.
You could also use array_map to do it.
Try something like this (it's probably not the most efficient way, but it should work):
for ($i = count($all_countries) - 1; $i >= 0; $i--) {
if (in_array($all_countries[$i]['countries']['countryName'], $prohibited_countries) {
unset($all_countries[$i]);
}
}
If you wanted to use the Set class included in CakePHP, you could definitely reduce the simplicity of your country array with Set::combine( array(), key, value ). This will reduce the dimensionality (however, you could do this differently as well. It looks like your country array is being created by a Cake model; you could use Model::find( 'list' ) if you don't want the multiple-dimension resultant array... but YMMV).
Anyway, to solve your core problem you should use PHP's built-in array_filter(...) function. Manual page: http://us3.php.net/manual/en/function.array-filter.php
Iterates over each value in the input
array passing them to the callback
function. If the callback function
returns true, the current value from
input is returned into the result
array. Array keys are preserved.
Basically, pass it your country array. Define a callback function that will return true if the argument passed to the callback is not on the list of banned countries.
Note: array_filter will iterate over your array, and is going to be much faster (execution time-wise) than using a for loop, as array_filter is a wrapper to an underlying C function. Most of the time in PHP, you can find a built-in to massage arrays for what you need; and it's usually a good idea to use them, just because of the speed boost.
HTH,
Travis

Resources