An array arrives with some or all of the following values, in any order. What's the best way to order them in ascending size order? So starting with small and ending with XXL. I can usort but am a bit lost as to how the elements should be ordered in my user defined function
Small
XXL
Medium
Large
XL
EDIT: left out some info so created new question Custom ordering array with key / value pairs
EDIT2: Full code
print_r($sizes);
$sorted_sizes = $this->sort_sizes(array_unique($sizes));
print_r($sorted_sizes);
function sort_sizes($sizes)
{
return uasort($sizes, array($this, 'cmp'));
}
function cmp($a,$b)
{
$sizeArray = array( 'Small' => 0, 'Medium' => 1, 'Large' => 2, 'XL' => 3, 'XXL' => 4);
return $sizeArray[$a] - $sizeArray[$b];
}
This outputs:
Array
(
[66-507cddcd16d9786abafccfa78b19acf8] => XL
[64-507cddcd16d9786abafccfa78b19acf8] => medium
[65-507cddcd16d9786abafccfa78b19acf8] => large
[63-507cddcd16d9786abafccfa78b19acf8] => small
)
and print_r($sorted_sizes) just gives output "1"
Updated answer according to full code
The first issue here is that you're returning the result of uasort():
function sort_sizes($sizes)
{
return uasort($sizes, array($this, 'cmp'));
}
That's wrong, because uasort() does not return the sorted array. It modifies the same variable that you pass as a parameter, and returns a boolean value. That's why you see 1 as output.
Make the method accept $sizes by reference:
function sort_sizes(array &$sizes)
{
uasort($sizes, array($this, 'cmp'));
}
Then call it like so:
print_r($sizes);
$sorted_sizes = array_unique($sizes);
$this->sort_sizes($sorted_sizes);
print_r($sorted_sizes);
Here's your cmp() method, with added support for case-insensitive sorting:
function cmp($a, $b)
{
$sizes = array('small' => 0, 'medium' => 1, 'large' => 2, 'xl' => 3, 'xxl' => 4);
return $sizes[strtolower($a)] - $sizes[strtolower($b)];
}
Old answer
Try this. Use uasort() instead if you want to maintain key-value pairs:
function sort_sizes($a, $b) {
// Map the sizes to an ordered sequence of ints
static $sizes = array('small' => 0, 'medium' => 1, 'large' => 2, 'xl' => 3, 'xxl' => 4);
// Find the difference, using the sizes as keys to the above array
return $sizes[strtolower($a)] - $sizes[strtolower($b)];
}
$arr = array('Small', 'XXL', 'Medium', 'Large', 'XL');
print_r($arr); // Before sorting
uasort($arr, 'sort_sizes');
print_r($arr); // After sorting
Output:
Array
(
[0] => Small
[1] => XXL
[2] => Medium
[3] => Large
[4] => XL
)
Array
(
[0] => Small
[2] => Medium
[3] => Large
[4] => XL
[1] => XXL
)
You can do this using array_multisort:
$orderIndex = array_flip(array('Small','Medium','Large','XL','XXL'));
$arr = array('Small','XXL','Medium','Large','XL');
array_multisort(array_map(function($val) use ($orderIndex) { return $orderIndex[$val]; }, $arr), $arr);
Here array_map is used with an anonymous function to build an array of the “weight” of each value in $arr. That array is then used to order the values in $arr.
This is basically the same as BoltClock suggested just with doing the comparison on the already calculated “weights”.
Do the sorting in two phases. First, convert text to a convenient numeric value. In this moment you should decide if "L" and "Large" map to the same value or not. Then reorder the array based on those numeric conversions.
Related
Original array:
$resolutions = array (
"Desktop monitor" => array (
//Standard 4:3
"800x600" => "800x600",
"1024x768" => "1024x768",
"1600x1200" => "1600x1200",
//Wide 16:10
"960x600" => "960x600",
"1280x800" => "1280x800",
"1440x900" => "1440x900",
"1680x1050" => "1680x1050",
"1920x1200" => "1920x1200",
),
"Apple" => array (
"DeviceX" => "2048x1536",
"DeviceY" => "1024x768",
),
);
Wanted array:
$resolutions = array (
"Desktop monitor" => array (
"800x600" => "800x600",//Standard 4:3
"960x600" => "960x600",//Wide 16:10
"1024x768" => "1024x768",//Standard 4:3
"1280x800" => "1280x800",//Wide 16:10
"1440x900" => "1440x900",//Wide 16:10
"1600x1200" => "1600x1200",//Standard 4:3
"1680x1050" => "1680x1050",//Wide 16:10
"1920x1200" => "1920x1200",//Wide 16:10
),
"Apple" => array (
"DeviceY" => "1024x768",
"DeviceX" => "2048x1536",
),
);
What I have tried:
foreach ($resolutions as $screen => $resolution) {
foreach($resolution as $key => $val) {
$newarray[$key] = $row[$val];
}
array_multisort($newarray, SORT_DESC, $resolution);
}
I thought that I'm half way to end, but the code above gave me some non sense (for me), ie: first goes resolution 1024x768, then 1280x800, followed by 1440x900 and in the end is 800x600.
The problem is that you are sorting alphabetically. You will need to calculate the size of each resolution to sort them correctly. This will require writing a custom comparison function that calculate the resolution sizes and compare them.
foreach ($resolutions as &$resolution)
uasort($resolution, function ($a, $b) { return array_product(explode('x', $a)) - array_product(explode('x', $b)); });
This is a quick hack looking specifically for the Desktop monitor array key. ksort will sort by array key, but it doesn't sort well when you have an x in the middle of some numbers. Here's my solution:
Loop through array
Build new array with only first half of resolution as array key
Use ksort to sort new array
array_walk new array to create output array which looks like original
output original:
https://eval.in/71433
You could do the same thing for other arrays inside your original array e.g. Apple by using the value instead of the key as the resolution, and put the array_walk etc inside a loop.
Just looking at what you've put there, two things stand out:
You're looping through the outer array and sorting a single-dimensional array using multisort; I would recommend just using asort().
Not quite sure what's going on there with $resolutions being passed in as the thrid paramter. It wants an array of sort flags.
try:
foreach ($resolutions as $resolution) {
asort($resolution);
}
I've researched topics similar to this but not exactly what I'm looking to do.
I have a multidimensional array like the following.
[code] => BILL
[assets] => Array
(
[en] => Array
(
[datatype] => My Assets
[data] => Array
(
[Corporate Equity] => 41
[Global Equity] => 24
[Fixed Income – Government] => 22
[Fixed Income – Corporate] => 8.1
[Other] => 3.57
)
)
)
I'd like to remove the first inner array, but preserve the values. Shift them up one level in the array so that it looks like this.
[code] => BILL
[assets] => Array
(
[datatype] => My Assets
[data] => Array
(
[Corporate Equity] => 41
[Global Equity] => 24
[Fixed Income – Government] => 22
[Fixed Income – Corporate] => 8.1
[Other] => 3.57
)
)
This is just the beginning of the array, there are other instances of the same key [en] at the same level.
I've tried unset, array_shift and others but I need to keep the contents of [en], just shift them up one level in the array.
You can use array_map which returns an array which contains all elements of the previous array after applying the function.
In this case it will simply take the array at index en and add it's contents to the new array.
http://php.net/manual/en/function.array-map.php
$arr = array('assets' => array(
'en' => array(
'datatype' => 'My Assets',
'data' => array(
'Corporate Equity' => 41,
'Global Equity' => 24,
'Fixed Income – Government' => 22,
'Fixed Income – Corporate' => 8.1,
'Other' => 3.57
)
)
));
$new_arr = array_map(function ($e) {
return $e['en'];
}, $arr);
A simple solution that assumes the key to always be en and the subkeys to always be (only) datatype and data:
$assets['datatype'] = $assets['en']['datatype'];
$assets['data'] = $assets['en']['data'];
unset( $assets['en'] );
This code could be problematic for you in the future if that array structure ever changes (it lacks extensibility), but it gets you what you want given the information you have provided.
array_shift is the opposite of array_pop. Used in stack/queue like structures for removing the fist element http://php.net/manual/en/function.array-shift.php
What you want to do is flatten the array. But if you want to keep all the other sub-arrays as you mentioned, you might look up array_merge.
I faced the same scenario after using reader to read xml file, the returned array was having inserted 0 key array in each level like the following one:
'config' =>
0 =>
'products' =>
0 =>
'media' =>
.
.
.
so I built a small function to get rid of a specific key and shift up its child's in a two dimensions array, in my case the key was 0. hopping this would help somebody also.
public function clearMaps(&$maps, $readerMaps, $omittedKey)
{
if (is_array($readerMaps)) {
foreach ($readerMaps as $key => $map) {
if ($key !== $omittedKey) {
$maps[$key] = [];
$this->clearMaps($maps[$key], $readerMaps[$key], $omittedKey);
} else {
$this->clearMaps($maps, $readerMaps[$key], $omittedKey);
}
}
} else {
$maps = $readerMaps;
}
}
// $maps: cleaned array, will start as empty array
// $readerMaps: array needs to be cleaned
// $omittedKey: array key to git rid of.
// first call is clearMaps([], $readerMaps, 0);
$final = array(array([0] => 123, [1] => 3, [2] => "true"),
array([0] => 9, [1] => 4, [3] => "false"),
array([0] => 541, [1] => 1, [3] => "false"));
I tried using php's sort function so that the $final array will be sorted from lowest to highest based upon the $final[$i][0] value, but it doesn't seem to work. This is just a quick example for the sake of in simplicity. In the real problem that I'm working with, the arrays that are inside of the $final array range from having as little as 3 elements to as many as 7. Don't know if that has any effect on the problem or not.
That's what PHP's usort function is for:
usort($final, function ($a, $b)
{
return $a[0] < $b[0] ? -1 : 1;
});
See it here in action: http://codepad.viper-7.com/omXxkR
I have an array of 15000 elements each of which is an array of 4 elements. I want to sort by the second element of the 4. Originally I made the original array's keys the second element and then k-sorted but unfortunately, some of the second elements are duplicates and since one key can't refer to multiple elements i lost some elements in transition. I could bubble sort by the second element but I'm looking for something that runs at least on the order of nlog(n). Can anyone think of a good algorithm (or possibly a function in php that I don't know about) that can sort by the second element? Thank you!
I think you can use usort and define the cmp_function to use the second element.
As others have stated, usort or uasort to maintain the array keys is what you want:
<?php
$myArray = array(
'fruits' => array(
array('apples', 'oranges', 'bananas')
),
'vegetables' => array(
array('lettuce', 'carrots', 'peas')
),
'monkeys' => array(
array('Curious George', 'Amy', 'Marcel')
)
);
// PHP 5.3+ example using a closure
uasort($myArray, function(array $a, array $b) {
// Use whatever sorting algorithm you like
return strnatcasecmp($a[1], $b[1]);
});
var_export($myArray);
Running the above will output:
array (
'monkeys' =>
array (
0 =>
array (
0 => 'Curious George',
1 => 'Amy',
2 => 'Marcel',
),
),
'vegetables' =>
array (
0 =>
array (
0 => 'lettuce',
1 => 'carrots',
2 => 'peas',
),
),
'fruits' =>
array (
0 =>
array (
0 => 'apples',
1 => 'oranges',
2 => 'bananas',
),
),
)
Here's an example that doesn't use a closure for pre PHP 5.3:
sortFunc(array $a, array $b)
{
return strnatcasecmp($a[1], $b[1]);
}
uasort($myArray, 'sortFunc');
I don't know what the internal implementation of usort is, but I'd bet it's better than bubblesort (it's probably quicksort).
The thing is I have to create a 2D matrix in php where each row and column must have a key. I tried to do this but what happens is that a 2-D array is created which does not resemble a matrix. I used the following code:
$x=$row['start_id'];
$y=$row['dest_id'];
$d=$row['distance'];
$this->map[$x][$y]=$d;
Here map is the intended matrix. The intention of this code is to create an adjacency matrix and then fill the unset cells with maximum distance. $x, $y and $d in above code are derived from result of a mysql query.
Sample Output:
Array (
[10010012] => Array (
[10010013] => 2
[10010016] => 8
)
[10010016] => Array (
[10010015] => 5
)
[10010013] => Array (
[10010014] => 7
[10010016] => 3
)
[10010014] => Array (
[10010015] => 2
)
)
Now the problem is that I am not able to fill the empty cells
e.g. row key =>[10010012] and column key=>[10010015] (Not able to set value)
Any help is appreciated. If possible also mention how to traverse through such matrices.
I am a relative beginner and have tried my best to explain my problem. However if you find any shortcomings please point them out.
Edit: The matrix is not a square one.
That would be
$this->map[10010012][10010015]= MAX_DISTANCE;
On the other hand, why do you want to set all empty/non-existing cell to MAX_DISTANCE? You can leave the map incomplete and whenever a cell does not exist you assume MAX_DISTANCE as its value.
edit: simple example
define('MAX_DISTANCE', PHP_INT_MAX);
$map = array(
10010012 => array ( 10010013 => 2, 10010016 => 8),
10010016 => array ( 10010015 => 5 ),
10010013 => array ( 10010014 => 7, 10010016 => 3),
10010014 => array ( 10010015 => 2 )
);
function getValue(&$map, $x, $y) {
return isset($map[$x][$y]) ? $map[$x][$y] : MAX_DISTANCE;
}
function setValue(&$map, $x, $y, $value) {
if ( !isset($map[$x]) ) {
$map[$x] = array($y => $value);
}
else {
$map[$x][$y] = $value;
}
}
// get an "existing" value from $map
echo getValue($map, 10010012, 10010016), "\n";
// get a "non-existing" value from $map
echo getValue($map, 10010014, 10010016), "\n";
// set a "new" value
setValue($map, 10010014, 10010016, 5);
// $map has been altered
var_dump($map[10010014]);
prints
8
2147483647
array(2) {
[10010015]=>
int(2)
[10010016]=>
int(5)
}