Sorting a multi-dimentsional associative array of arrays - php

Like the title sais I have no idea where to start and using asort() and sort() are not helping in the way I thought it would. Essentially I have an array as such:
$array = array(
'array_c' => array(
'array_b' => (
array('object' => 'e some Object'),
array('object' => 'b some Object'),
),
'array_a' => (
array('object' => 'awesome Object'),
),
),
'array_a' => array(
'array_e' => (
array('object' => 'e some Object'),
),
'array_a' => (
array('object' => 'b awesome Object'),
);
);
);
So I was looking at asort as I want to keep the associations the same, The function I have started writing is:
function sort_me(some_array){
$new_array = asort(some_array);
return $new_array;
}
this function then takes in $array['array_c'] so that you get back an alphabetically sorted array that looks like:
'array_c' => array(
'array_a' => (
array('object' => 'awesome Object'),
),
'array_b' => (
array('object' => 'b some Object'),
array('object' => 'e some Object'),
),
),
can some one tell me why my function is not working? Am I misunderstanding the power of asort?

ksort is the way to go, but ksort does not return a newly sorted array:
http://us.php.net/ksort
it returns a bool -> true if the array could be sorted, otherwise false...
this code snipped should do what you need:
ksort($array);
foreach($array as $key=>$value){
ksort(value);
$array[$key]=$value;
}
print_r($array);

Related

PHP: not looping inside a foreach

I have this array:
$flavours = array (
0 => array(799390 => 'Banana',),
1 => array(799391 => 'Chocolate',)
);
And now I inside a foreach looping a set from my database. I get 'Banana' from the database and I need the array to give me 799390.
I have tried:
‌‌array_search('Banana', $flavours);
but it does not work unless I add:
‌‌array_search('Banana', $flavours[0]);
But I cannot add [0] as I won't be able to tell in which position 'Banana' flavour is in the array.
Any solution apart from looping again inside a foreach??
Thanks a ton
First, we can use array_walk_recursive() to flatten your array, which just gets rid of your nested arrays.
Next, we use array_flip() to swap the keys/values in the flattened array. This makes it easier to grab the ID for a specific term.
<?php
$flavours = [
[799390 => 'Banana'],
[799391 => 'Chocolate']
];
//flatten array
//Produces: Array ( [799390] => Banana [799391] => Chocolate )
array_walk_recursive($flavours,
function($v, $k) use (&$temp) {
$temp[$k] = $v;
}
);
//flip array. Swaps keys with values.
//Produces: Array ( [Banana] => 799390 [Chocolate] => 799391 )
$flavours = array_flip($temp);
Now you can get the ID pretty easily, such as $flavours['Banana'].
If you have a very very large array, this method may become slow. However, I tested this with 100,000 values on my cheap webhost, and ran this method a few times (20-25 times). It is finishing consistently at around (usually under) 0.1 milliseconds, which is about 0.0014 seconds.
You can insert an if statement to set a condition for your loop as
foreach ($flavours as $key => $value) {
if($key = array_search('Banana', $value)){
echo $key;
}
}
Output
799390
If the word being searched for is a different case many of the usual array methods will not work when trying to find the match, using preg_grep however will allow matches to be found in a case-insensitive manner.
function findflavour( $search, $haystack ){
foreach( $haystack as $index => $arr ){
$res=preg_grep( sprintf( '#%s#i', $search ), $arr );
if( !empty( $res ) ) return array_search( array_values( $res )[0], $arr );
}
return false;
}
$search='BaNanA';
$flavours=array(
array( 799390 => 'Banana' ),
array( 799391 => 'Chocolate' ),
array( 729361 => 'Chilli' ),
array( 879695 => 'Apple' ),
array( 995323 => 'Avacado' ),
array( 528362 => 'Orange' ),
array( 723371 => 'Cherry' ),
);
printf( 'Key:%s', findflavour( $search, $flavours ) );
If there might be multiple elements in the source array with the same value but different IDs a slightly different version of the findflavour function
function findflavour( $search, $haystack, $multiple=false ){
$keys=[];
foreach( $haystack as $index => $arr ){
$res=preg_grep( sprintf( '#%s#i', $search ), $arr );
if( !empty( $res ) ) {
$key=array_search( array_values( $res )[0], $arr );
if( $multiple )$keys[]=$key;
else return $key;
}
}
return $multiple ? $keys : false;
}
$multiple=true;
$search='AVacAdo';
$flavours=array(
array( 799390 => 'Banana' ),
array( 799391 => 'Chocolate' ),
array( 291333 => 'Avacado' ),
array( 729361 => 'Chilli' ),
array( 879695 => 'Apple' ),
array( 995323 => 'Avacado' ),
array( 528362 => 'Orange' ),
array( 723371 => 'Cherry' ),
);
printf( 'Key(s): %s', print_r( findflavour( $search, $flavours, $multiple ), true ) );

How to write convert an anonymous functions to a regular function while using use $variable

I'm trying to figure out how to convert the array_map portion with the anonymous function to just a function while using the use function to be able to support php 5.2 but i keep getting an error. Here is my current code.
<?php
$collection = array();
$op_field = array(
'fname' => 'sarmen',
'lname' => 'b',
'age' => 33,
'gender' => 'male'
);
$nf_field = array(
'type' => 'human',
'age' => 30,
'gender' => 'male',
'ethnicity' => 'american'
);
array_map(function($op, $nf) use (&$collection)
{
$collection[] = array(
'op' => $op,
'nf' => $nf
);
}, $op_field, $nf_field);
print_r($collection);
I've tried
function mapping($op, $nf)
{
$collection[] = array(
'op' => $op,
'nf' => $nf
);
return $collection;
}
array_map(mapping($op, $nf), use ($&collection), $op_field, $nf_field);
But that just gives a parse error. Any idea on how it would be written? I really appreciate it.
There's no need to pass in a reference to $collection, instead just do the following:
function mapping($op, $nf)
{
return array(
'op' => $op,
'nf' => $nf
);
}
$collection = array_map('mapping', $op_field, $nf_field);
Yields:
Array
(
[0] => Array
(
[op] => sarmen
[nf] => human
)
[1] => Array
(
[op] => b
[nf] => 30
)
[2] => Array
(
[op] => 33
[nf] => male
)
[3] => Array
(
[op] => male
[nf] => american
)
)
Hope this helps :)
First off you have to pass the callback as string when you want to use a normal function. Second there is no use before PHP 5.3, so you would have to use global.
And if you put everything together you will get:
function callbackFunction($op, $nf){
global $collection;
$collection[] = array(
'op' => $op,
'nf' => $nf
);
}
array_map("callbackFunction", $op_field, $nf_field);

Processing data from query

I'm writing a 'get' function in a model class in codeigniter, but I need to process some of the data as it's returned, ideally without a whole bunch of overhead.
function get_answers($p)
{
$result = $this->db->get_where('answer', array('a_upid_fk' => $p))->result();
// foreach ($result->answer as $ans) {
// $result->answers = explode( '|', $ans, -1 );
// }
return $result;
}
The results look like this:
array (
0 =>
stdClass::__set_state(array(
'aid' => '742',
'a_upid_fk' => '231',
'answer' => '4555|||',
'a_qid_fk' => '70',
'created' => '2012-04-20 15:35:38',
'last_modified' => '2012-04-20 15:36:11',
'revision' => '1',
)),
1 =>
stdClass::__set_state(array(
'aid' => '743',
'a_upid_fk' => '231',
'answer' => NULL,
'a_qid_fk' => '71',
'created' => '2012-04-20 15:35:38',
'last_modified' => '2012-04-20 15:35:38',
'revision' => '1',
)) ...
the problem is the answer is stored as a pipe delimited list of answers, but I want the function to return it as an exploded array instead. I'm not sure of the syntax and how to create and replace or append the array to the array of objects I've pasted above.
You can see some code I've been trying commented out.
Ideally instead of 'answer' => '4555|||',
I would like to have
'answer' => array (
0 => '4555',
1=> '',
2=> '')
I have no problem making the array using explode but I'm not sure how to modify the original codeigniter active-record result.
Loop thru your sub-arrays:
$answer_array = explode('|', $answer);
foreach ($aswer_array as $instance)
{
$result[] = $instance;
}
Define __set_state()), call it using - $subarray = $result[0][$object->answer].

function calling itself

again with another problem.
public function __construct() {
$_GET = $this->clean($_GET);
$_POST = $this->clean($_POST);
...
}
public function clean($data) {
if (is_array($data)) {
foreach ($data as $key => $value) {
unset($data[$key]);
$data[$this->clean($key)] = $this->clean($value);
}
} else {
$data = htmlspecialchars($data, ENT_COMPAT, 'UTF-8');
}
return $data;
}
i dont understand why $data[$this->clean($key)] = $this->clean($value); is calling its own function. what is the point of doing this? the advantages
thanks,
daniel
It's a technique called recursion. This particular function descends into the structure until it is dealing with very simple data and sanitizes it all.
Given this:
$arr = array (
'<foo' => array(
'bat' => 'OMGWTFBBQ!!><!?!'
),
'bar' => 'baz'
);
It would start:
is arr an array?
is <foo an array? (no)
<foo becomes <foo
<foo used as a key
is the value of <foo an array? (yes)
Is bat an array? (no)
bat remains as is (still has htmlspecialchars called, but it does not change anything))
Is 'OMGWTFBBQ!!><!?!' an array (no)?
'OMGWTFBBQ!!><!?!' is converted to 'OMGWTFBBQ!!><!?!'
'OMGWTFBBQ!!><!?!' is used for the value for bat.
Is 'bar' an array? (no)
bar returned as is (like bat above)
Is baz an array? (no)
baz returned as is.
You can think of it this way
$arr = array (
'<foo' => array(
'bat' => 'OMGWTFBBQ!!><!?!'
),
'bar' => 'baz'
);
///////////////////////////////////////////
array (
clean('<foo') => array(
'bat' => 'OMGWTFBBQ!!><!?!'
),
'bar' => 'baz'
);
///////////////////////////////////////////
array (
'<foo' => clean( array(
'bat' => 'OMGWTFBBQ!!><!?!'
)),
'bar' => 'baz'
);
///////////////////////////////////////////
array (
'<foo' => array(
clean( 'bat' ) => 'OMGWTFBBQ!!><!?!'
)),
'bar' => 'baz'
);
///////////////////////////////////////////
array (
'<foo' => array(
'bat' => clean( 'OMGWTFBBQ!!><!?!' )
)),
'bar' => 'baz'
);
///////////////////////////////////////////
array (
'<foo' => array(
'bat' => 'OMGWTFBBQ!!><!?!'
)),
clean( 'bar' ) => 'baz'
);
///////////////////////////////////////////
array (
'<foo' => array(
'bat' => 'OMGWTFBBQ!!><!?!'
)),
'bar' => clean( 'baz' )
);
///////////////////////////////////////////
return array (
'<foo' => array(
'bat' => 'OMGWTFBBQ!!><!?!'
)),
'bar' => 'baz'
);
clean function uses htmlspecialchars to clean html chars from string. But if $data is an array it calls itself for cleaning all of its keys and values. This is why clean is recursive.
The advantage of this approach is clean function works for string and array transparently.
It's called recursion.
In your example the $_GET and $_POST arrays may contain elements which are also arrays which themselves can contain arrays (ad infinitum). So you don't know how many sub-arrays you will have to clean up.
To solve this problem the function clean is written in a manner that it calls itself when it occurs a "sub array" while cleaning the current array. It's like a loop just for nested data structures.

Sorting an array by a certain key

I have the following array:
Array
(
[Places] => Array
(
[public] => 0
[entities] => Array
(
...
)
)
[Issues] => Array
(
[public] => 1
[entities] => Array
(
...
)
)
[Source] => Array
(
[public] => 0
[entities] => Array
(
...
)
)
)
I would like to be able to sort by the array by the public key. I know I may have to use either ksort or usort but I am unsure on how to implement this.
Any ideas would be great thanks!
usort($array, function ($a, $b) { return $a["public"] - $b["public"]; });
Here's an interesting link: http://www.the-art-of-web.com/php/sortarray/
I would try a
usort(usort(array, function), function);
I can try a sample code upon request, but the information is already there for the taking.
take a look at this, using array_multisort :
$test = array(
'Places' => array(
'public' => 0,
'entities' => array(
)
),
'Issues' => array(
'public' => 1,
'entities' => array()
),
'Source' => array(
'public' => 0,
'entities' => array()
)
);
echo '<pre>';
print_r($test);
echo '</pre>';
array_multisort($test,SORT_ASC,$test);
echo '<pre>';
print_r($test);
echo '</pre>';
The general way to do this using array_multisort is to place your sorting value into its own array and then sort both arrays using that as the primary sorting array.
Try the following:
$test = array(
'Places' => array(
'public' => 0,
'entities' => array(
)
),
'Issues' => array(
'public' => 1,
'entities' => array()
),
'Source' => array(
'public' => 0,
'entities' => array()
)
);
echo '<pre>';
print_r($test);
echo '</pre>';
$sort = array();
foreach ($test as $k => $a) {
$sort[$k] = $a['public'];
}
// placing $sort first in array_multisort causes $test to be sorted in same order as the values in $sort
array_multisort($sort,SORT_ASC,$test);
echo '<pre>';
print_r($test);
echo '</pre>';
You could use usort with callback function.
function cmp($a, $b) {
return $a['public'] == $b['public'] ? 0 : $a['public'] > $b['public'] ? 1 : -1;
}
usort($array, "cmp");
Try this:
$code = "return (-1*strnatcmp(\$a['public'], \$b['public']));";
uasort($array, create_function('$a,$b', $code));

Categories