Shuffle only same value associative array in PHP - php

My array is
$rank = [
'20' => 1,
'30' => 1,
'40' => 2,
'50' => 2,
'60' =>3,
];
expected op
$rank = ['30' => 1,'20' => 1,'50' => 2,'40' => 2,'60' =>3];
I want ranking random means only rank 1(value) should shuffle, later rank 2 should shuffle. later 3 and so on.. how to do it this in PHP?

function shuffle_array($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;
}
echo "<pre>"; print_r(shuffle_array($rank));

First step can be to collect all values(say roll numbers) in an array( say a set) grouped by ranks nicely.
Now, get all the unique ranks from your array.
Sort the ranks(skip this step and just run a for loop starting from 1 if ranks are sequential).
Loop over this unique ranks and get all roll numbers according to the rank in hand.
Shuffle() them and add all of them to your result.
Snippet:
<?php
function getRandomizedSortedData($ranks){
$rank_set = [];
foreach($ranks as $roll_no => $rank){
if(!isset($rank_set[ $rank ])){
$rank_set[$rank] = [];
}
$rank_set[$rank][] = $roll_no;
}
$unique_ranks = array_unique($ranks);
sort($unique_ranks);
$result = [];
foreach($unique_ranks as $rank){
$roll_nos = $rank_set[$rank];
shuffle($roll_nos);
foreach($roll_nos as $roll_no){
$result[$roll_no] = $rank;
}
}
return $result;
}
Demo: http://sandbox.onlinephpfunctions.com/code/86c0ead227a06dda5c048dc9316c53788838bd65

you can use php function array_filter():
$rank = [
'20' => 1,
'30' => 2,
'40' => 3,
'50' => 2,
'60' => 1,
];
function filterRank1($value) {
return $value === 1;
}
function filterRank2($value) {
return $value === 2;
}
...
$rank1 = array_filter($rank, 'filterRank1');
$rank2 = array_filter($rank, 'filterRank2');
...

Related

Multidimensional Array Search by Multiple Keys given as Variable?

I have a multidimensional array which has keys and key has values or have another array with keys and values so I want to search by keys but in input like 230 is user input
and it will go to 3 then 4 then 1 if result is a value but not an array it must print the value like
input = 230 result should be = "3-4-1"
so I need to str_split the number and search it 1 by 1 if first number is array then look for second kinda
edit1 = I found the way to split the key
//edit1
$keys = "021";
$keysSplit =str_split($keys, strlen($keys)/strlen($keys));
echo $keys[0];
//edit 1 ends
$arr = [0 => [0=>"1-1", 1 => "1-2" , 2=>"1-3", 3=>[0=>"1-4-1", 1 => "1-4-2" , 2=>"1-4-3"]],
1 => [0=>"2-1", 1 => "2-2" , 2=>"2-3"],
2 => [0=>"3-1", 1 => "3-2" , 2=>"3-3", 3=>[0 =>"3-4-1" , 1=> "3-4-2"]],
];
$keys = "021";
function searchByKey($array , $keys){
$result = [];
$keys = "021";
$keys =str_split($keys, strlen($keys)/strlen($keys));
$key1 = $keys[0];
$key2 = $keys [1];
$key3 = $keys [2];
foreach ($array as $key1 => $value){
if (is_array($value)){
$key1 = null;
$key1 = $key2;
$key2 = $key3;
return searchByKey($value , $key1);
}
else {
$result=$value;
echo $result;
}
}
}
$arr = searchByKey($arr, $keys);
The function only works as key and value given and it will print every key and value on the key it asked first so its not the thing I wanted to do could anyone help and explain?
Answer given by #Anggara I made it in to function ;
$input = "11";
function searchByNumber($array, $input){
$result = $array;
for ($i = 0; $i < strlen($input); $i++) {
if (is_array($result)) {
$result = $result[$input[$i]];
} else {
$result = "Does not exists";
break;
}
}
echo $result;
}
$arr = searchByNumber($arr, $input);
You can access char in string like accessing an array. For example:
$input = "230";
// $input[0] is "2"
// $input[1] is "3"
// $input[2] is "0"
So my approach is to loop for each character in input key, and look for corresponding value in $arr. Each iteration will set found array element into variable $result. If the searched key does not exist (ex: "021"), print error message.
<?php
$arr = [
0 => [
0 => "1-1",
1 => "1-2",
2 => "1-3",
3 => [
0 => "1-4-1",
1 => "1-4-2",
2 => "1-4-3"
]
],
1 => [
0 => "2-1",
1 => "2-2",
2 => "2-3"
],
2 => [
0 => "3-1",
1 => "3-2",
2 => "3-3",
3 => [
0 => "3-4-1",
1 => "3-4-2"
]
],
];
$input = "230";
$result = $arr;
for ($i = 0; $i < strlen($input); $i++) {
if (is_array($result)) {
$result = $result[$input[$i]];
} else {
$result = 'Can not traverse path';
break;
}
}
echo $result;
After splitting the keys
for($i=0;$i<strlen($keys);$i++){
$arr = $arr[$keys[$i]];
}
if(is_array($arr)){
echo json_encode($arr);
}else{
echo $arr;
}
You need a loop, which will go through the keys one by one and assigning into the array.

php sum values based on same same ids in an array

I want to sum input['amount'] in given array only if input['ids'] are same. Please tell any best solution. Thanks
$input = [
'ids' => [1, 2, 3, 1],
'amount' => [50, 100, null, 100]
];
$result = [];
foreach ($input['ids'] as $key => $value) {
echo $key . ' = ' . $value . '<br>';
if($key == $key) {
$result['amount'] += $input['amount'][$key];
}
}
// I want this result form input array. thanks
$result = [
'ids' => [1, 2, 3],
'amount' => [150, 100, null]
];
// new array, ids => amount
$ids = array_unique($input['ids']);
// intialise to 0
foreach($ids as $id) {
$tempMap[$id] = 0;
}
// sum
foreach(array_values($input['amount']) as $i => $amount) {
if (is_null($amount)) {
$tempMap[ $input['ids'][$i] ] = $amount;
} else {
$tempMap[ $input['ids'][$i] ] += $amount;
}
}
$result['ids'] = array_keys($tempMap);
$result['amount'] = array_values($tempMap);
Use an associative array to hold the sums.
$sums = [];
foreach ($input['ids'] AS $index => $id) { // loop over the IDs
if ($input['amount'][$index] !== null) { // check if the corresponding amount is not null
$sums[$id] += $input['amount'][$index]; // Add it to the associative array that's keyed by ID
}
}
$result = [];
$result['ids'] = array_unique($input['ids']); // find all the different IDs
$result['amount'] = array_map(function($id) use($sums) {
return $sums[$id] ?? null; // for each ID, return the sum, or null if all the corresponding amounts were null
}, $result['ids']);

call_user_func_array with array_multisort [duplicate]

This question already has answers here:
Sort array using array_multisort() with dynamic number of arguments/parameters/rules/data
(5 answers)
Closed 2 years ago.
I have the problem with sort direction. I try to sort multi-dimensional array with direction. I can't use array_multisort() directly, because I don't know how many parametrs will be. I use call_user_func_array('array_multisort', $params); And it works, but I can't set sort direction (SORT_ASC,SORT_DESC). How can I set sort direction for call_user_func_array('array_multisort', $params);?
Here is my code, you can try it
function get_fields($data, $order_by) {
$order_row = preg_split("/[\s,]+/", $order_by);
for ($i=0;$i<count($order_row);$i++) {
foreach ($data as $key => $row) {
$tmp[$i][$key] = $row[$order_row[$i]];
}
}
return $tmp;
}
function ordering($data, $order_by) {
$tmp = get_fields($data, $order_by);
$params = array();
foreach($tmp as &$t){
$params[] = &$t;
}
$params[1] = array("SORT_DESC","SORT_DESC","SORT_DESC","SORT_DESC"); // like that no warning but no sorting
$params[] = &$data;
call_user_func_array('array_multisort', $params);
return array_pop($params);
}
$data = array (
array('id' => 1,'name' => 'Barack','city' => 9),
array('id' => 7,'name' => 'boris','city' => 2),
array('id' => 3,'name' => 'coris','city' => 2),
array('id' => 3,'name' => 'coris','city' => 2)
);
$order_by = "city desc, name";
echo "<br>ORDER BY $order_by<br>";
$ordered = ordering($data, $order_by);
echo "<pre>";
var_dump($ordered);
echo "</pre>";
I want to do a sort like MySQL ORDER BY city DESC, name. It's my goal.
To be able to sort an array multiple times and achieve a result like ORDER BY city DESC, name ASC you need a function that does a stable sort.
As far as I know PHP doesn't have one so you have to sort it once with a comparator function like this
$data = array (
array('id' => 3,'name' => 'coris','city' => 2),
array('id' => 1,'name' => 'Barack','city' => 9),
array('id' => 7,'name' => 'boris','city' => 2),
array('id' => 3,'name' => 'coris','city' => 2),
);
$order_by = array(
'city' => array('dir' => SORT_DESC, 'type' => SORT_NUMERIC),
'name' => array('dir' => SORT_ASC, 'type' => SORT_STRING),
);
function compare($row1,$row2) {
/* this function should determine which row is greater based on all of the criteria
and return a negative number when $row1 < $row2
a positive number when $row1 > $row2
0 when $row1 == $row2
*/
global $order_by;
foreach($order_by as $field => $sort) {
if($sort['type'] != SORT_NUMERIC) {
// strings are compared case insensitive and assumed to be in the mb_internal_encoding
$cmp = strcmp(mb_strtolower($row1[$field]), mb_strtolower($row2[$field]));
} else {
$cmp = doubleval($row1[$field]) - doubleval($row2[$field]);
}
if($sort['dir'] != SORT_ASC) $cmp = -$cmp;
if($cmp != 0) return $cmp;
}
return 0;
}
usort($data,'compare');
I had the same problem. It seems that call_user_func_array() can't handle the constants.
I've solved this problem by dynamically building an argument string and evaluating this string:
$args = array($arr1, $arr2);
$order = array(SORT_ASC, SORT_DESC);
$evalstring = '';
foreach($args as $i=>$arg){
if($evalstring == ''){ $evalstring.= ', '; }
$evalstring.= '$arg';
$evalstring.= ', '.$order[$i];
}
eval("array_multisort($evalstring);");
I know eval() is evil and this is not a clean way, but it works ;-)
It is working for me :
$arrayThatNeedToSort = array('data..');
$multiSortprop = array(['data.....']=> SORT_DESC,['data.....'] => SORT_ASC)
$properties = array();
foreach ($multiSortprop as $sortArr => $sortArg) {
array_push($properties,$sortArr);
array_push($properties,$sortArg);
}
array_push($properties,$arrayThatNeedToSort);
array_multisort(...$properties);
var_dump(end($properties));

Split Array by Value

I'm working on a leader board that pulls the top scorers into first, second, and third place based on points. Right now I'm working with a sorted array that looks like this (but could be of infinite length with infinite point values):
$scores = Array
(
["bob"] => 20
["Jane"] => 20
["Jill"] => 15
["John"] => 10
["Jacob"] => 5
)
I imagine I could use a simple slice or chunk, but I'd like to allow for ties, and ignore any points that don't fit into the top three places, like so:
$first = Array
(
["bob"] => 20
["Jane"] => 20
)
$second = Array
(
["Jill"] => 15
)
$third = Array
(
["John"] => 10
)
Any ideas?
$arr = array(
"Jacob" => 5,
"bob" => 20,
"Jane" => 20,
"Jill" => 15,
"John" => 10,
);
arsort($arr);
$output = array();
foreach($arr as $name=>$score)
{
$output[$score][$name] = $score;
if (count($output)>3)
{
array_pop($output);
break;
}
}
$output = array_values($output);
var_dump($output);
$first will be in $output[0], $second in $output[1] and so on.. Code is limited to 3 first places.
ps: updated to deal with tie on the third place
I would do something like:
function chunk_top_n($scores, $limit)
{
arsort($scores);
$current_score = null;
$rank = array();
$n = 0;
foreach ($scores as $person => $score)
{
if ($current_score != $score)
{
if ($n++ == $limit) break;
$current_score = $score;
$rank[] = array();
$p = &$rank[$n - 1];
}
$p[$person] = $score;
}
return $rank;
}
It sorts the array, then creates numbered groups. It breaks as soon as the limit has been reached.
You can do it with less code if you use the score as the key of the array, but the benefit of the above approach is it creates the array exactly how you want it the first time through.
You could also pass $scores by reference if you don't mind the original getting sorted.
Here's my go at it:
<?php
function array_split_value($array)
{
$result = array();
$indexes = array();
foreach ($array as $key => $value)
{
if (!in_array($value, $indexes))
{
$indexes[] = $value;
$result[] = array($key => $value);
}
else
{
$index_search = array_search($value, $indexes);
$result[$index_search] = array_merge($result[$index_search], array($key => $value));
}
}
return $result;
}
$scores = Array(
'bob' => 20,
'Jane' => 20,
'Jill' => 15,
'John' => 10,
'Jacob' => 5
);
echo '<pre>';
print_r(array_split_value($scores));
echo '</pre>';
?>

PHP: Get two nearest neighbors from array?

I found this thread about picking the closest/nearest value from an array based upon a known value. What about if one wants to pick the two nearest values from an array looking at the same say?
$rebates = array(
1 => 0,
3 => 10,
5 => 25,
10 => 35)
$rebates = array(
1 => 0,
3 => 10,
5 => 25,
10 => 35);
function getArrayNeighborsByKey($array, $findKey) {
if ( ! array_key_exists($array, $findKey)) {
return FALSE;
}
$select = $prevous = $next = NULL;
foreach($array as $key => $value) {
$thisValue = array($key => $value);
if ($key === $findKey) {
$select = $thisValue;
continue;
}
if ($select !== NULL) {
$next = $thisValue;
break;
}
$previous = $thisValue;
}
return array(
'prev' => $previous,
'current' => $select,
'next' => $next
);
}
See it!
By "two nearest" you mean the two smaller than or equal to the value of $items?
Anyway, starting from the answer to that other thread, which is
$percent = $rebates[max(array_intersect(array_keys($rebates),range(0,$items)))];
You can go to
$two_nearest = array_slice(array_intersect(array_keys($rebates),range(0,$items)), -2);
$most_near = $rebates[$two_nearest[1]];
$less_near = $rebates[$two_nearest[0]];
This can probably be reduced to an one-liner using array_map, but I think it's overdone already.
$rebates = array(
1 => 0,
3 => 10,
5 => 25,
10 => 35)
$distances = array();
foreach($rebates as $key=>$item) {
if ($key == 5) continue;
$distances = abs($rebates[5] - $item);
}
sort($distances, SORT_NUMERIC)
Now you have an array with all the items in the array with their distance to $rebates[5] sorted. So you can get the two closest ones.
Or three closest ones. Whatever.
Just keep in mind that 2 items can have the same distance.

Categories