So I have an array of possible combinations:
$faces = array ('Bar', 'Cherry', 'Lemon', 'Seven', 'DoubleBar', 'TripleBar');
And an array of payouts
$payouts = array (
'Bar|Bar|Bar' => '5',
'Double Bar|Double Bar|Double Bar' => '10',
'Triple Bar|Triple Bar|Triple Bar' => '15',
'Cherry|Cherry|Cherry' => '20',
'Seven|Seven|Seven' => '70',
'Seven|Any|Any' => '70',
'Diamond|Diamond|Diamond' => '100',
);
And an array for every wheel (3 in total), how can I make weighted random combinations with Seven|Any|Any working properly?
I know I can just create two arrays 6^3 in size one representing the weights and the other array representing every possible combination and using something like this script, but isn't there a shorter, more efficient way?
function weighted_random_simple($values, $weights){
$count = count($values);
$i = 0;
$n = 0;
$num = mt_rand(0, array_sum($weights));
while($i < $count){
$n += $weights[$i];
if($n >= $num){
break;
}
$i++;
}
return $values[$i];
}
How about an array that correlates counts of possible results, rather than actual positions?
e.g. you do a run, get something like A/A/B, so you've got 2 A's, and look in the table
$payouts = array(
2 => array('Bar' => 10, 'Double Bar' => 20, etc...)
3 => array('Diamond' => 100, 'Bar' => 40, etc...)
);
Related
I'm currently working on a PHP project in which I need to merge 5 large arrays of arrays (around 16000 entries each) by a specific key value. With this I mean each array includes about 16000 entries, each being an array with key => value format, and I intend to merge all these arrays where the value for a given key matches. For example:
I have:
arr1=[[id=>1,name=>john,surname=>doe],[id=>2,name=>katy,surname=>johnson]];
arr2=[[id=>2,age=>23,adress=>something][id=>1,age=>43,adress=>something else]];
arr3=[[id=>1,employee_number=1234],[id=>3,employee_number=2345],[id=>2,emplyee_number=>6523]];
And plan to obtain:
arr=[[id=>1,name=>john,surname=>doe,age=43,adress=>something else],[id=>2,name=>katy,surname=>johnson,age=>23,adress=>something]];
My first attempt was to loop through every array using for loops and join the arrays when the given value for the id key was matched, but this proved highly inefficient for such a big number of entries (16000 for each array). Can someone suggest any better options to accomplish the given task?
Edit:
Even though the example has generic arrays names (arr1,arr2) the code I tried to implement was this, with the referred 5 arrays and looping through each to compare and tried to merge by the sku key value.
for($i=0;$i < sizeof($received_products); $i++){
for($j=0; $j < sizeof($received_descriptions); $j++){
for($k=0; $k < sizeof($received_multimedia); $k++){
for($l=0; $l < sizeof($received_stocks); $l++){
for($m=0; $m < sizeof($received_prices); $m++){
if(strcmp(strcmp($received_products[$i]['sku'],$received_descriptions[$j]['sku'])&&strcmp($received_multimedia[$k]['sku'],$received_stocks[$l]['sku']),$received_prices[$m]['sku'])==0){
$all_products[$i]=array_merge($received_products[$i],$received_descriptions[$j],$received_multimedia[$k],$received_stocks[$l],$received_prices[$m]);
}
}
}
}
}
}
You should be able to do this with two nested loops:
<?php
$arr1 = [['id' => 1, 'name' => 'john', 'surname' => 'doe'], ['id' => 2, 'name' => 'katy', 'surname' => 'johnson']];
$arr2 = [['id' => 2, 'age' => 23, 'address' => 'something'], ['id' => 1, 'age' => 43, 'address' => 'something else']];
$arr3 = [['id' => 1, 'employee_number' => 1234], ['id' => 3, 'employee_number' => 2345], ['id' => 2, 'emplyee_number' => 6523]];
$buffer = [];
// For each of the records sets...
foreach ([$arr1, $arr2, $arr3] as $currRecordSet)
{
// Loop through the records...
foreach ($currRecordSet as $currRecord)
{
// If we do not have an entry in our buffer for ths ID yet, create one
$currKey = '_' . $currRecord['id'];
if (!array_key_exists($currKey, $buffer))
{
$buffer[$currKey] = [];
}
// Merge this record with the values in the buffer
$buffer[$currKey] = array_merge($buffer[$currKey], $currRecord);
}
}
// Get the bare array of combined entries without our ugly buffer keys
$combinedRecords = array_values($buffer);
print_r($combinedRecords);
I'm pretty new to PHP and i'm stuck in the following scenario. I have an array with some values and I want to get the max value in the array set.
For ex:
$array = array(
0 => array(
'1' => '123',
'2' => '120',
'3' => '30',
'4' => '150'
),
1 => array(
'1' => '123',
'2' => '122',
'3' => '30',
'4' => '158'
),
2 => array(
'1' => '123',
'2' => '129',
'3' => '300',
'4' => '150'
)
);
The value i'm expecting is 300.
I have tried the following code and i don't know how to get the maximum value from all the sub arrays.
$max = 0;
foreach( $array as $k => $v ){
//this is where i need help
}
Any kind of help would be highly appreciated.
You can flatten your array first using array_merge(...$array), then just use the max() function:
$new_array = array_merge(...$array);
echo max($new_array);
Demo
I took #Hirumina's solution and just set $max = $y if $y was > $max
$max = 0;
foreach( $array as $k => $v ) {
foreach($v as $x => $y) {
if($y > $max){
$max = $y;
}
}
}
echo $max;
$new_array = array_map(function($value){
return max($value);
}, $array);
echo max($new_array);
Here array_map function will get max value from individual $array and store in $new_array.
Then max($new_array) will give you max value.
Consider this collection below:
$collection = [
[1 => 10.0, 2 => 20.0, 3 => 50.0, 4 => 80.0, 5 => 100.0],
[3 => 20.0, 5 => 20.0, 6 => 100.0, 7 => 10.0],
[1 => 30.0, 3 => 30.0, 5 => 10.0, 8 => 10.0]
];
Consider this theorical output based on the intersection of the Arrays contained into $collection, considering their array keys with respective values based on the average of the single values:
$output = Array ( 3 => 33.3333, 5 => 43.3333 );
Can this problem be resolved with a native PHP function like array_intersect_* in an elegant way?
If not, can you suggest me an elegant solution that doesn't necessarily need an outer ugly foreach?
Keep in mind that the number of arrays that need to be intersected is not fixed. It can be 2 input arrays as it can be 1000 input arrays.
Keys will be integers at all times, and Values will be floats or integers at all times.
In other words:
$collection = [
$arr1 = [ ... ];
$arr2 = [ ... ];
$arr3 = [ ... ];
...
$arrn = [ ... ];
];
$output = [ intersected and weighted array based (on comparison) on keys from $arr1 to $arrn, and (on values) from the value averages ];
Count the input array once.
$n = count($collection);
Compute the intersection of all the sub-arrays by key.
$intersection = array_intersect_key(...$collection);
// PHP5: $intersection = call_user_func_array('array_intersect_key', $input);
Build your result by averaging the column from the input array for each key from the intersection.
$output = [];
foreach ($intersection as $key => $value) {
$output[$key] = array_sum(array_column($collection, $key)) / $n;
}
If you really want to completely avoid foreach you can use array_map instead.
$output = array_map(function($key) use ($collection, $n) {
return array_sum(array_column($collection, $key)) / $n;
}, array_keys($intersection));
But in my opinion, this just adds unnecessary complexity.
Note: The values in $intersection will be single values from the first sub-array, but they don't really matter; they're disregarded when generating the output. If it bothers you to have a useless $value variable in the foreach, then you can do foreach (array_keys($intersection) as $key) instead, but I opted for avoiding an unnecessary function call.
Can this problem be resolved with a native PHP function like array_intersect_* in an elegant way?
Well, elegance is in the eye of the developer. If functional-style programming with no new globally-scoped variables equals elegance, then I have something tasty for you. Can a native array_intersect_*() call be leveraged in this task? You bet!
There's a big lack in PHP native functions on intersects - #Maurizio
I disagree. PHP has a broad suite of powerful, optimized, native array_intersect*() and array_diff*() functions. I believe that too few developers are well-acquainted with them all. I've even build a comprehensive demonstration of the different array_diff*() functions (which can be easily inverted to array_intersect*() for educational purposes).
Now, onto your task. First, the code, then the explanation.
Code: (Demo)
var_export(
array_reduce(
array_keys(
array_intersect_ukey(
...array_merge($collection, [fn($a, $b) => $a <=> $b])
)
),
fn($result, $k) => $result + [$k => array_sum(array_column($collection, $k)) / count($collection)],
[]
)
);
The first subtask is to isolate the keys which are present in every row. array_intersect_ukey() is very likely the best qualified tool. The easy part is the custom function -- just write the two parameters with the spaceship in between. The hard part is setting up the variable number of leading input parameters followed by the closure. For this, temporarily merge the closure as an array element onto the collection variable, then spread the parameters into the the native function.
The payload produced by #1 is an array consisting of the associative elements from the first row where the keys were represented in all rows ([3 => 50.0, 5 => 100.0]). To prepare the data for the next step, the keys must be converted to values -- array_keys() is ideal because the float value are of no further use.
Although there is an equal number of elements going into and returning in the final "averaging step", the final result must be a flat associative array -- so array_map() will not suffice. Instead, array_reduce() is better suited. With the collection variable accessible thanks to PHP7.4's arrow function syntax, array_column() can isolate the full column of data then the averaging result pushed as an associative element into the result array.
I guess it could be done like this:
<?php
$intersecting_arrays = Array (
0 => Array ( 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100 ),
1 => Array ( 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10 ),
2 => Array ( 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10 )
);
$temp = $intersecting_arrays[0];
for($i = 1; $i < count($intersecting_arrays); $i++) {
$temp = array_intersect_key($temp, $intersecting_arrays[$i]);
}
$result = Array();
foreach(array_keys($temp) as $key => $val) {
$value = 0;
foreach($intersecting_arrays as $val1) {
$value+= $val1[$val];
}
$result[$key] = $value / count($intersecting_arrays);
}
print_r($temp);
print_r($result);
https://3v4l.org/j8o75
In this manner it doesn't depend on how much arrays you have.
Here you get the intersection of keys in all arrays and then count an average using collected keys.
Ok, with an unknown number of input arrays, I would definitively go with two nested foreach loops to combine them first - getting an unknown number into array_merge_recursive or similar is going to be difficult.
$input = [
0 => [ 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100],
1 => [ 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10],
2 => [ 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10]
];
$combined = [];
foreach($input as $array) {
foreach($array as $key => $value) {
$combined[$key][] = $value;
}
}
$averages = array_map(function($item) {
return array_sum($item)/count($item);
}, $combined);
var_dump($averages);
https://3v4l.org/hmtj5
Note that this solution doesn't need to check for array vs single integer in the array_map callback, because unlike array_merge_recursive, $combined[$key][] inside the loops sees to it that even the keys with just one value will have that value in an array.
EDIT:
but keep in mind that not all the keys are going to be taken into account
Ah, ok, so you want averages only for those keys that occurred more than once. That can easily be fixed by filtering the combined array before using array_map on it:
$combined = array_filter($combined, function($v, $k) {
return count($v) != 1;
}, ARRAY_FILTER_USE_BOTH );
Integrated into above solution: https://3v4l.org/dn5ro
EDIT #2
[Andreas' comment] I think "one" should not be in output since it is not in all three arrays.
Ah, I see ... couldn't tell that was the actually desired result even from the example :-) Then my filtering has to be modified a little bit again, and take the number of input arrays into account:
$combined = array_filter($combined, function($v, $k) use($input) {
return count($v) == count($input);
}, ARRAY_FILTER_USE_BOTH );
https://3v4l.org/9H086
You can merge the arrays to one and use array_sum and count() to get the average.
$arr1 = Array ( 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100 );
$arr2 = Array ( 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10 );
$arr3 = Array ( 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10 );
$array = array_merge_recursive($arr1,$arr2,$arr3);
$key= "two";
If(is_array($array[$key])){
$avg = array_sum($array[$key])/count($array[$key]);
}Else{
$avg = $array[$key];
}
Echo $avg;
https://3v4l.org/pa3PH
Edit to follow $collection array.
Try this then. Use array column to grab the correct key and use array_sum and count to get the average.
$collection = array(
Array ( 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100 ),
Array ( 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10 ),
Array ( 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10 ));
$key= "three";
$array = array_column($collection, $key);
If(count($array) != 1){
$avg = array_sum($array)/count($array);
}Else{
$avg = $array[0];
}
Echo $avg;
https://3v4l.org/QPsiS
Final edit.
Here I loop through the first subarray and use array column to find all the matching keys.
If the count of keys is the same as the count of collection the key exsists in all subarrays and should be "saved".
$collection = array(
Array ( 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100 ),
Array ( 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10 ),
Array ( 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10 ));
Foreach($collection[0] as $key => $val){
$array = array_column($collection, $key);
If(count($array) == count($collection)){
$avg[$key] = array_sum($array)/count($array);
}
}
Var_dump($avg);
https://3v4l.org/LfktH
Consider two simple arrays:
<?php
$array1 = array(
'blue' => 5,
'green' => array(
'square' => 10,
'sphere' => 0.5,
'triangle' => 3
),
'red' => array(
'circle' => 1000,
),
'black' => 4,
);
$array2 = array(
'blue' => 1,
'green' => array(
'square' => 11,
'circle' => 5,
),
'purple' => 10,
'yellow' => array(
'triangle' => 4
),
'black' => array(
'circle' => 6,
),
);
I need to mathmatically add together in a recursive way, each value from each $array1 and $array2.
Preserve keys
Where a key does not exist in $array1 but does exist in $array2, the final array would simply contain the value of $array2 (and the other way around as well)
Where they exist in both, the numeric values would be added +
Non-numeric values wouldn't be touched
If a value on $array1 points to another sub-array, and in $array2 it points to a value, the end value would result in that key containing a subarray that contains the values from $array1 plus a new key/value using the parent name and it's value (see black in the example)
Should be able to work at virtually unlimited nesting
To clarify, e.g. if we said
<?php
$final = array_merge_special($array1, $array2);
// We would end up with, if you var_export()'d final, something like:
// (Note: Hope I didn't make mistakes in this or it will be confusing,
// so expect mild human error)
$final = array(
'blue' => 6, // 5+1
'green' => array(
'square' => 21, // (10+11)
'sphere' => 0.5, // only in $array1
'triangle' => 3 // only in $array1
'circle' => 5, // only in $array2
),
'purple' => 10, // only in $array2
'yellow' => array( // only in $array2
'triangle' => 4
),
'red' => array( // only in $array1
'circle' => 1000,
),
'black' => array(
'circle' => 6, // untouched, while $black is present in both, the $array1 value does not have a 'circle' key, and is actually only a key/value (see below)
'black' => 4, // the key/value from $array1 that was not a subarray, even though it was a subarray in $array2
),
);
This seems outragously daunting to me. I know I could loop over one array and get easily recursively add the values, and I have this working (somewhat), but it's when I get into special rules (such as ones for black) that I can't even imagine how broken code would look. There has to be a way to do this would looping over each array individually and unset()'ing values to merge?
You would use array_walk_recursive (see: See php Manual here) and possibly array_merge_recursive. I'd have to think it through further to get the full picture.
OK, decided that this wouldn't work! Array_walk_recursive doesn't pass keys that hold arrays to the function. This problem kept flowing aroung in my brain, so I just had to write a function to do it! Here it is:
function dosum($arin) {
$arout = array();
foreach ($arin as $key1 => $item1) {
$total = 0;
if(is_array($item1)) {
foreach($item1 as $key2 => $item2) {
if(is_numeric($key2))
$total += $item2;
else
if(is_array($item2))
$arout[$key1] = dosum(array($key2 => $item2));
else
$arout[$key1][$key2] =$item2;
}
if($total)
if(isset($arout[$key1]))
$arout[$key1][$key1] = $total;
else
$arout[$key1] = $total;
}
else
$arout[$key1] = $item1;
}
return $arout;
}
For the 2 arrays given, you would use it like this:
print_r(dosum(array_merge_recursive($array1, $array2)));
I would like to compare two arrays: one is containing the list of possible options and the other array contains priorities.
This is how the two arrays are organized :
foreach ($varsA as $varA) {
foreach ($varsB as $varB) {
$options[$varA][$varB] = $id;
$priority[$varA] = $priority + $priority[$varA];
}
}
this is what $options contains:
array (
1 =>
array (
33307 => 'w',
33313 => '7',
),
2 =>
array (
33307 => 'w',
33313 => '7',
),
3 =>
array (
33307 => 'w',
33313 => '7',
),
4 =>
array (
33307 => '4',
33313 => '7',
),
)
and this is what $priority contains:
array (
1 => 5,
2 => 9,
3 => 9,
4 => 5,
)
I would like to duplicate the duplicates values from $options and keep the one with the uniques with the lowest priority:
The output would be: Array (1, 4) Because 1, 2, 3 are not unique and 1 has the smallest priority.
I was using the following function to remove duplicates but I don't know how I can adapt it to deal with priorities:
super_magic($options) {
$result = array_map("unserialize", array_unique(array_map("serialize", $options)));
foreach ($result as $key => $value) {
if ( is_array($value) ) {
$result[$key] = super_magic($value);
}
}
return $result;
}
This sounds like a very specialized scenario without a foundation specific to PHP. With that being said, i would look into http://php.net/manual/en/ref.array.php that page for specifics on the PHP functions at your disposal for using PHP arrays. I would look into the functions like uksort() and uasort()
for ($x = 1; $x <= count($options); $x++) {
for ($i = 1; $i <= count($options); $i++) {
if (array_key_exists($x, $options) && array_key_exists($i, $options)) {
if ($x != $i) {
$diff = array_diff($options[$x], $options[$i]);
if(empty($diff)) {
if ($priority[$x] >= $priority[$i]){
unset($options[$x]);
}
}
}
}
}
}