I'm trying to sort an array with school class names however when I use an alphabetical sorting function they wouldn't sort by grade year.
Alphabetical sorting:
Array
(
"LA2A",
"LB1A",
"LG2A",
"LG3A",
"LH2A",
"LH3A",
"LH4A",
"LH5A",
"LV4A",
"LV5A",
"LV6A"
)
This is what I would like to achieve:
Array
(
"LB1A",
"LA2A",
"LG2A",
"LH2A",
"LG3A",
"LH3A",
"LH4A",
"LV4A",
"LH5A",
"LV5A",
"LV6A"
)
So, how can I sort an array (in PHP) by first the third character, then the fourth and finally the second character.
Demo using usort
$test = array(
"LA2A",
"LB1A",
"LG2A",
"LG3A",
"LH2A",
"LH3A",
"LH4A",
"LH5A",
"LV4A",
"LV5A",
"LV6A"
);
//sort by first the third character, then the fourth and finally the second character.
function mySort($left, $right) {
$left = $left[2].$left[3].$left[1];
$right = $right[2].$right[3].$right[1];
return strcmp($left, $right);
}
usort($test, 'mySort');
$test is now :
Array (
[0] => LB1A
[1] => LA2A
[2] => LG2A
[3] => LH2A
[4] => LG3A
[5] => LH3A
[6] => LH4A
[7] => LV4A
[8] => LH5A
[9] => LV5A
[10] => LV6A
)
The easiest way to do this is to apply something like the https://en.wikipedia.org/wiki/Schwartzian_transform
$arr = ...;
function add_key($x) { return $x[2] . $x[3] . $x[1] . $x; }
function rem_key($x) { return substr($x, 3); }
$tmp = array_map("add_key",$arr);
sort($tmp);
$res = array_map("rem_key",$tmp);
add_key adjusts each string by copying the sort key to the front. Then we sort it. rem_key gets rid of the key.
Related
I am trying to reduce an associative array to pairs of unique values, whereby the keys are letters to be paired and the values are their count()s.
Each pair cannot contain the same letter twice such as AA or BB.
It is permissible for more than one occurrence of the same pair.
e.g. AC, DC, AC, DC are all valid in the resultant array
Every time a letter is picked, its associated number decreases by one until none remain, or
any odd/leftover letters should be flagged and not ignored, e.g. Array([paired]=>Array(...) [unpaired]=>Array(...))
Sample input:
Array(
[A] => 8
[B] => 16
[C] => 15
[D] => 4
[E] => 1
)
Possible output:
[
'paired' => [
'BD', 'AE', 'BC', 'AC', 'BC', ...
],
'unpaired' => [
'C' => 4
]
]
I would prefer two unique letters to be chosen at random, but if that is too hard then the resultant array should be easily shuffle()-able
I have tried various combinations of array_reduce(), array_map(), array_walk(), array_unique() etc., even Pear's Math_Combinatorics, but all to no avail.
Here is a function that will generate the results you want. It iterates over the letters array, filtering out letters which have been completely used, until there is only one letter which is unused. The list of pairs and the unused letter are then returned in an array:
function random_pairs($letters) {
$pairs = array();
$letters = array_filter($letters);
while (count($letters) > 1) {
$keys = array_keys($letters);
shuffle($keys);
list($letter1, $letter2) = array_slice($keys, 0, 2);
$pairs[] = "$letter1$letter2";
$letters[$letter1]--;
$letters[$letter2]--;
$letters = array_filter($letters);
}
return array('pairs' => $pairs, 'unpaired' => $letters);
}
Sample usage:
$letters = array('A' => 8, 'B' => 16, 'C' => 15, 'D' => 4, 'E' => 1);
print_r(random_pairs($letters));
Sample output:
Array
(
[pairs] => Array
(
[0] => CB
[1] => CE
[2] => CD
[3] => BD
[4] => CB
[5] => DC
[6] => AB
[7] => CA
[8] => DA
[9] => BC
[10] => BA
[11] => BA
[12] => AB
[13] => BA
[14] => BC
[15] => AB
[16] => BC
[17] => CB
[18] => CB
[19] => CB
[20] => CB
)
[unpaired] => Array
(
[C] => 2
)
)
Demo on 3v4l.org
Here is a recursive technique. It uses array_rand() to grab two unique keys at a time. If you need the paired keys to be randomly arranged, then call shuffle() upon them. What I mean is, shuffle() might be omittable depending on your expectations.
An advantage in using array_rand() is that array_keys() doesn't need to be called since it returns random keys.
I have deliberately designed this snippet to unconditionally declare the unpaired subarray -- even if it is empty.
Code: (Demo)
function randomPairs(array $letters, array $result = []) {
if (count($letters) < 2) {
$result['unpaired'] = $letters;
return $result;
}
$keys = array_rand($letters, 2);
shuffle($keys);
$result['paired'][] = $keys[0] . $keys[1];
--$letters[$keys[0]];
--$letters[$keys[1]];
return randomPairs(array_filter($letters), $result);
}
Without recursion: (Demo)
function randomPairs(array $letters): array {
$result['paired'] = [];
while (count($letters) > 1) {
$keys = array_rand($letters, 2);
shuffle($keys);
$result['paired'][] = $keys[0] . $keys[1];
--$letters[$keys[0]];
--$letters[$keys[1]];
$letters = array_filter($letters);
}
$result['unpaired'] = $letters;
return $result;
}
I propose you this one :
I've just seen Nick's answer, but it do 15 minutes i'm trying xD I want to post my answer :p
Based on a "pickRandomValues" function, i made it dynamic, and you can as well choose the number of letters you want
function pickRandom(&$array = [], $count = 1) {
$valuesFound = [];
if (count($array) >= $count) {
for ($i = 0; $i < $count; $i++) {
$index = rand(0, count($array) - 1);
$tmp = array_splice($array, $index, 1);
$valuesFound[] = $tmp[0];
}
}
return $valuesFound;
}
And you body code
$tmpArr = [];
foreach ($a as $letter => $count) {
$t = [];
$tmpArr = array_merge($tmpArr, array_pad($t, $count, $letter));
}
$pairs = [];
while (count($tmpArr) >= $numberOfLetters) {
$pick = pickRandom($tmpArr, $numberOfLetters);
$pairs[] = implode($pick);
}
$finalArray = [
"paired" => $pairs,
"unpaired" => array_count_values($tmpArr)
];
So, it give something like this :
http://sandbox.onlinephpfunctions.com/code/3b143396b7267bd6029ff613746d6c047557a282
I am getting an array like below after i formatted my result set into the below format.
Array
(
[0] => xxx#2456
[1] => xxx#2345
[2] => xxy#1234
[3] => xxy#123
[4] => xyz#3456
);
I want the result array from the above array like below. I am thinking how ot proceed using PHP string functions or is there any other way.
Note: There may be so many array elements like the above but i want unique elements after we added last digits after '#'.
Array
(
[0] => xxx#4801
[1] => xxy#1464
[2] => xyz#3456
);
Any ideas how to proceed....... In the above output array format xxx#4801 is sum of 2345 and 2456 of Input array.......
You can try
$unique = array_reduce($data, function ($a, $b) {
$c = explode("#", $b);
if (isset($a[$c[0]])) {
$d = explode("#", $a[$c[0]]);
$a[$c[0]] = $c[0] . "#" . ($c[1] + $d[1]);
} else {
$a[$c[0]] = $b;
}
return $a;
}, array());
echo "<pre>";
print_r(array_values($unique));
Output
Array
(
[0] => xxx#4801
[1] => xxy#1357
[2] => xyz#3456
)
Simple Online Demo
You can use array_unique() function and reindex it with array_values() Find the code for example.
array_unique($array)
array_values(array_unique($array));
I have an array of values which are either all-letters or all-numbers and need to sort them in an ascending fashion. Additionally, I want all-numeric values to be moved to the end of the array so that they occur after all of the non-numeric values.
$test = ["def", "yz", "abc", "jkl", "123", "789", "stu"];
If I run sort() on it I get:
Array
(
[0] => 123
[1] => 789
[2] => abc
[3] => def
[4] => jkl
[5] => stu
[6] => yz
)
but I'd like to see:
Array
(
[0] => abc
[1] => def
[2] => jkl
[3] => stu
[4] => yz
[5] => 123
[6] => 789
)
I tried array_reverse(), but that didn't seem to change anything. I'm at a loss for how to get the numbers last, but in ascending order.
What you need is sort but with a custom comparison function (usort).
The following code will get it done:
function myComparison($a, $b){
if(is_numeric($a) && !is_numeric($b))
return 1;
else if(!is_numeric($a) && is_numeric($b))
return -1;
else
return ($a < $b) ? -1 : 1;
}
$test = array("def", "yz", "abc", "jkl", "123", "789", "stu");
usort ( $test , 'myComparison' );
You could convert your numbers to integers before sorting:
$array = array("def", "yz", "abc", "jkl", "123", "789", "stu");
foreach ($array as $key => $value) {
if (ctype_digit($value)) {
$array[$key] = intval($value);
}
}
sort($array);
print_r($array);
Output:
Array
(
[0] => abc
[1] => def
[2] => jkl
[3] => stu
[4] => yz
[5] => 123
[6] => 789
)
In the following code is separate the data in two arrays: one is numerical the other is not and sort it and merge it.
$arr1 = $arr2 = array();
$foreach ($arr as $val) {
if (is_numeric($val)) {array_push($arr2, $val); }
else {array_push($arr1, $val);}
}
so you have to separate arrays whit numeric and non-numeric
sort($arr2);
sort($arr1);
$test = array_merge($arr2,$arr1);
You could do this using usort and a custom comparison function, but this sounds like more trouble than it's worth. I'd use sort, and then handle that output accordingly. It's not clear how you want to use it, but a simple way might be:
sort($test);
foreach ($test as $index=>$value) {
if (is_numeric($value)) {
$test[] = $value;
unset($test[$index]);
} else {
continue;
}
}
usort will probably be faster and it's going to do the comparisions once, while the other solutions mentioned thus far may be a little slower as they require iterating over some or all of the array before or after the sort
You do not need to engage any iterated processing for this task -- and therefore should not.
Use array_multisort() to sort the array numerically, then as strings (normally).
Code: (Demo)
array_multisort($test, SORT_NUMERIC, $test);
I got this array:
array (
0 => 'K.',
1 => 'Vrachtschip',
2 => 'L.',
3 => 'Gevechtsschip',
4 => 'Z.',
5 => 'Gevechtsschip',
6 => 'Kruiser',
7 => 'Slagschip',
8 => 'Bommenwerper',
9 => 'Vernietiger',
10 => 'Interceptor.',
)
of can I merge the items [0] with [1], because K. vrachtschip must be together.
same ass [2] and [3]; and [4] with [5]. if there is 1 letter and then a dot (k.) it must be merged with the following array item.
Anyone that can help me :)?
How about:
$arr = array (
'K.',
'Vrachtschip',
'L.',
'Gevechtsschip',
'Z.',
'Gevechtsschip',
'Kruiser',
'Slagschip',
'Bommenwerper',
'Vernietiger',
'Interceptor',
'B.',
);
$concat = '';
$result = array();
foreach ($arr as $elem) {
if (preg_match('/^[A-Z]\.$/', $elem)) {
$concat = $elem;
continue;
}
$result[] = $concat.$elem;
$concat = '';
}
if ($concat) $result[] = $concat;
print_r($result);
output:
Array
(
[0] => K.Vrachtschip
[1] => L.Gevechtsschip
[2] => Z.Gevechtsschip
[3] => Kruiser
[4] => Slagschip
[5] => Bommenwerper
[6] => Vernietiger
[7] => Interceptor
[8] => B.
)
Try to use a regular expression to test all entries of your array.
If an occurence is founded, concat the value of your entrie with the next.
I would try something like this:
for($idx=0, $max = count($array_in); $idx < $max; $idx++)
{
if(preg_match('/^[a-z]\.$/i', $array_in[$idx]))
{
$array_out[] = $array_in[$idx].$array_in[$idx+1];
$idx++;
}
else
{
$array_out[] = $array_in[$idx];
}
}
I'd probably do the following (pseudo code):
Create empty array for result
Iterate the original array
For each value: does it match /^[a-z]\.$/i?
If yes, see if original array contains a next element?
If yes, concatenate the two items and add to resulting array, skip next item in loop
If no (pt. 4 or 5) add directly to resulting array.
I've been looking on google for the answer but can't seem to find something fool-proof and cant really afford to mess this up (going live into a production site).
What I have is an advanced search with 20+ filters, which returns an array including an ID and a Distance. What I need to do is shuffle these results to display in a random order every time. The array I have that comes out at the moment is:
Array (
[0] => Array ( [id] => 1 [distance] => 1.95124994507577 )
[1] => Array ( [id] => 13 [distance] => 4.75358968511882 )
[2] => Array ( [id] => 7 [distance] => 33.2223233233323 )
[3] => Array ( [id] => 21 [distance] => 18.2155453552336 )
[4] => Array ( [id] => 102 [distance] = 221.2212587899658 )
)
What I need to be able to do is randomise or order of these every time but maintain the id and distance pairs, i.e.:
Array (
[4] => Array ( [id] => 102 [distance] = 221.2212587899658 )
[1] => Array ( [id] => 13 [distance] => 4.75358968511882 )
[3] => Array ( [id] => 21 [distance] => 18.2155453552336 )
[2] => Array ( [id] => 7 [distance] => 33.2223233233323 )
[0] => Array ( [id] => 1 [distance] => 1.95124994507577 )
)
Thanks :)
The first user post under the shuffle documentation:
Shuffle associative and
non-associative array while preserving
key, value pairs. Also returns the
shuffled array instead of shuffling it
in place.
function shuffle_assoc($list) {
if (!is_array($list)) return $list;
$keys = array_keys($list);
shuffle($keys);
$random = array();
foreach ($keys as $key) {
$random[$key] = $list[$key];
}
return $random;
}
Test case:
$arr = array();
$arr[] = array('id' => 5, 'foo' => 'hello');
$arr[] = array('id' => 7, 'foo' => 'byebye');
$arr[] = array('id' => 9, 'foo' => 'foo');
print_r(shuffle_assoc($arr));
print_r(shuffle_assoc($arr));
print_r(shuffle_assoc($arr));
As of 5.3.0 you could do:
uksort($array, function() { return rand() > rand(); });
Take a look to this function here :
$foo = array('A','B','C');
function shuffle_with_keys(&$array) {
/* Auxiliary array to hold the new order */
$aux = array();
/* We work with an array of the keys */
$keys = array_keys($array);
/* We shuffle the keys */`enter code here`
shuffle($keys);
/* We iterate thru' the new order of the keys */
foreach($keys as $key) {
/* We insert the key, value pair in its new order */
$aux[$key] = $array[$key];
/* We remove the element from the old array to save memory */
unset($array[$key]);
}
/* The auxiliary array with the new order overwrites the old variable */
$array = $aux;
}
shuffle_with_keys($foo);
var_dump($foo);
Original post here :
http://us3.php.net/manual/en/function.shuffle.php#83007
function shuffle_assoc($array)
{
$keys = array_keys($array);
shuffle($keys);
return array_merge(array_flip($keys), $array);
}
I was having a hard time with most of the answers provided - so I created this little snippet that took my arrays and randomized them while maintaining their keys:
function assoc_array_shuffle($array)
{
$orig = array_flip($array);
shuffle($array);
foreach($array AS $key=>$n)
{
$data[$n] = $orig[$n];
}
return array_flip($data);
}
Charles Iliya Krempeaux has a nice writeup on the issue and a function that worked really well for me:
function shuffle_assoc($array)
{
// Initialize
$shuffled_array = array();
// Get array's keys and shuffle them.
$shuffled_keys = array_keys($array);
shuffle($shuffled_keys);
// Create same array, but in shuffled order.
foreach ( $shuffled_keys AS $shuffled_key ) {
$shuffled_array[ $shuffled_key ] = $array[ $shuffled_key ];
} // foreach
// Return
return $shuffled_array;
}
Try using the fisher-yates algorithm from here:
function shuffle_me($shuffle_me) {
$randomized_keys = array_rand($shuffle_me, count($shuffle_me));
foreach($randomized_keys as $current_key) {
$shuffled_me[$current_key] = $shuffle_me[$current_key];
}
return $shuffled_me;
}
I had to implement something similar to this for my undergraduate senior thesis, and it works very well.
Answer using shuffle always return the same order. Here is one using random_int() where the order is different each time it is used:
function shuffle_assoc($array)
{
while (count($array)) {
$keys = array_keys($array);
$index = $keys[random_int(0, count($keys)-1)];
$array_rand[$index] = $array[$index];
unset($array[$index]);
}
return $array_rand;
}
$testArray = array('a' => 'apple', 'b' => 'ball', 'c' => 'cat', 'd' => 'dog');
$keys = array_keys($testArray); //Get the Keys of the array -> a, b, c, d
shuffle($keys); //Shuffle The keys array -> d, a, c, b
$shuffledArray = array();
foreach($keys as $key) {
$shuffledArray[$key] = $testArray[$key]; //Get the original array using keys from shuffled array
}
print_r($shuffledArray);
/*
Array
(
[d] => dog
[a] => apple
[c] => cat
[b] => ball
)
*/
I tried the most vote solution didn't popular shuffle list. This is the change I made to make it work.
I want my array key starting from 1.
$list = array_combine(range(1,10),range(100,110));
$shuffle_list = shuffle_assoc($list);
function shuffle_assoc($list)
{
if (!is_array($list)) return $list;
$keys = array_keys($list);
shuffle($list);
$random = array();
foreach ($keys as $k => $key) {
$random[$key] = $list[$k];
}
return $random;
}