Given an array of arrays like this:
$array = array(
0 => array (
0 => 35,
1 => 30,
2 => 39
),
1 => array (
0 => 20,
1 => 12,
2 => 5
),
...
n => array (
0 => 10,
1 => 15,
2 => 7
),
);
I have the need to find the entry in the array which is closer to given parameters
find($a, $b, $c) {
//return the closer entry to the input
}
For closer entry I mean the entry which has closer values to the ones gave in input, e.g. passing (19, 13, 3) it should return $array[1]
The way in which I do the calculation at the moment is looping through the whole array, keeping a variable $distance which starts from -1, and a temporary $result variable. For each element I calculate the distance
$dist = abs( subarray[0] - $a ) + abs ( subarray[1] - $b ) + abs( subarray[2] - $c )
and if the calculated distance is equal to -1 or lower than the variable $distance which is out of the loop, I assign the new distance to the varaible and I save the corresponding array in the $result variable. At the end of the loop I end up having the value I need.
Also, one of the values can be empty: e.g. (19, 13, false) should still return $array[1] and the calculation should then ignore the missing parameter - in this case the distance is calculated as
$dist = abs( subarray[0] - $a ) + abs ( subarray[1] - $b );
ignoring the values of subarray[2] and $c.
The problem is, even if my code is working, it took too much time to execute as the size of the array can easily go up to many hundred thousands elements. We are still talking about milliseconds, but for various reasons this is still unacceptable.
Is there a more effective way to do this search in order to save some time?
A custom function - maybe there is a better way but check it out :
In a few words :
Search all the items and find in percentage the difference between the number it checks($mArray[0...3]) and the number you gave($mNumbersToFind[0...3]. Add all the three number's (of each element) possibilities - find the max - keep the position and return the array.
$array = array(
array (
0 => 13,
1 => 15,
2 => 4
),
array (
0 => 20,
1 => 12,
2 => 5
),
array (
0 => 13,
1 => 3,
2 => 15
),
);
$mNumbersToFind = array(13,3,3);
$mFoundArray = find($mNumbersToFind, $array);
echo "mFinalArray : <pre>";
print_r($mFoundArray);
function find($mNumbersToFind, $mArray){
$mPossibilityMax = count($mNumbersToFind);
$mBiggestPossibilityElementPosition = 0;
$mBiggestPossibilityUntilNow = 0;
foreach($mArray as $index => $current){
$maxPossibility = 0;
foreach($current as $subindex => $subcurrent){
$mTempArray[$index][$subindex]['value'] = $subcurrent - $mNumbersToFind[$subindex];
$percentChange = (1 - $mTempArray[$index][$subindex]['value'] / $subcurrent) * 100;
$mTempArray[$index][$subindex]['possibility'] = $percentChange;
$maxPossibility += $percentChange/$mPossibilityMax;
}
$mTempArray[$index]['final_possibility'] = $maxPossibility;
if($maxPossibility > $mBiggestPossibilityUntilNow){
$mBiggestPossibilityUntilNow = $maxPossibility;
$mBiggestPossibilityElementPosition = $index;
}
}
echo "mTempArray : <pre>"; // Remove this - it's just for debug
print_r($mTempArray); // Remove this - it's just for debug
return $mArray[$mBiggestPossibilityElementPosition];
}
Debug Output ($mTempArray) :
mTempArray :
Array
(
[0] => Array
(
[0] => Array
(
[value] => 0
[possibility] => 100
)
[1] => Array
(
[value] => 12
[possibility] => 20
)
[2] => Array
(
[value] => 1
[possibility] => 75
)
[final_possibility] => 65
)
[1] => Array
(
[0] => Array
(
[value] => 7
[possibility] => 65
)
[1] => Array
(
[value] => 9
[possibility] => 25
)
[2] => Array
(
[value] => 2
[possibility] => 60
)
[final_possibility] => 50
)
[2] => Array
(
[0] => Array
(
[value] => 0
[possibility] => 100
)
[1] => Array
(
[value] => 0
[possibility] => 100
)
[2] => Array
(
[value] => 12
[possibility] => 20
)
[final_possibility] => 73.333333333333
)
)
Final Output :
mFinalArray :
Array
(
[0] => 13
[1] => 3
[2] => 15
)
I basically used a concept of proximity (lesser distance total for each array) and returned that. The code was made in a way that can improve very well in so many routines.
PS: I didn't used advanced functions or other things because you are concerned about performance issues. It's most simplest routine I could did in a short period of time.
$array = array(
0 => array (
0 => 35,
1 => 30,
2 => 39
),
1 => array (
0 => 20,
1 => 12,
2 => 5
),
);
$user = array(19,13,3);
function find($referencial, $input){
$totalRef = count($referencial);
if (is_array($referencial)){
for ($i = 0; $i < $totalRef; $i++) {
if (is_array($referencial[$i])){
$totalSubRef = count($referencial[$i]);
$proximity = array();
for ($j = 0; $j < $totalSubRef; $j++) {
$proximity[$i] += abs($referencial[$i][$j] - $input[$j]);
}
if ($i > 0){
if ($maxProximity['distance'] > $proximity[$i]) {
$maxProximity['distance'] = $proximity[$i];
$maxProximity['index'] = $i;
}
} else {
$maxProximity['distance'] = $proximity[$i];
$maxProximity['index'] = $i;
}
}
}
return $maxProximity;
} else {
exit('Unexpected referencial. Must be an array.');
}
}
$found = find($array, $user);
print_r($found);
//Array ( [distance] => 4 [index] => 1 )
print_r($array[$found['index']]);
// Array ( [0] => 20 [1] => 12 [2] => 5 )
Related
This is how my array looks like :
Array
(
[0] => Array
(
[unit] => 10
[harga] => 15000
)
[1] => Array
(
[unit] => 7
[harga] => 10000
)
[2] => Array
(
[unit] => 12
[harga] => 123123
)
)
I want to unset the 0 key array when the unit is 0 and rearrange the key so the 1 key will replace the 0.
This is how I do it :
$jumlah_penjualan = $data - > unit;
while ($jumlah_penjualan > 0) {
$persediaan_pertama = $persediaan[0]['unit'];
$harga_persediaan = $persediaan[0]['harga'];
if ($persediaan_pertama < $jumlah_penjualan) {
$dijual = $persediaan_pertama;
$penjualan[] = array(
'unit' => $dijual,
'harga' => $harga_persediaan,
'total' => $dijual * $harga_persediaan);
$persediaan[0]['unit'] = $persediaan[0]['unit'] - $dijual;
} else {
$dijual = $jumlah_penjualan;
$penjualan[] = array(
'unit' => $dijual,
'harga' => $harga_persediaan,
'total' => $dijual * $harga_persediaan);
$persediaan[0]['unit'] = $persediaan[0]['unit'] - $dijual;
}
if ($persediaan[0]['unit'] == 0) {
unset($persediaan[0]);
$persediaan = array_values($persediaan);
}
$jumlah_penjualan = $jumlah_penjualan - $dijual;
}
But the result looks like it continues looping before rearranging the array.
This is how the array should look like after unset:
Array(
[0] => Array
(
[unit] => 9
[harga] => 123123
)
)
If you want to remove the first elements until the unit is not 0, you can
$arr = array
(
array
(
"unit" => 0,
"harga" => 15000
),
array
(
"unit" => 0,
"harga" => 10000
),
array
(
"unit" => 12,
"harga" => 123123
)
);
while( $arr[0]["unit"] == 0 ) { //Loop until $arr[0]["unit"] is not 0
unset($arr[0]); //Remove $arr[0] since unit is 0
$arr = array_values($arr); //Make Make element 1 to element 0
}
echo "<pre>";
print_r( $arr );
echo "</pre>";
This will result to:
Array
(
[0] => Array
(
[unit] => 12
[harga] => 123123
)
)
To remove the first element of an array and reindex the elements, use array_shift. First, check that the number of units are zero, then remove the first element if that's the case.
if ($arr[0]['unit'] == 0) {
array_shift($arr);
}
Since it's impossible to say what your other variables even mean because of the language, you probably want to move this outside of your while loop, so that the first element is only removed after you've processed the array.
I have a a array like below and i want min value and it's index for searching tax class id
Array
(
[tax_class_id] => Array
(
[0] => 12
[1] => 13
[2] => 13
)
[price] => Array
(
[0] => 6233
[1] => 3195
[2] => 19192
)
)
and i am searching least price and respective key in tax_class_id. In this Senario, i require lowest in price i.e 3195 and tax_id - 13 i.e key [1]
My Code is
$prod_total = array();
for($i = 1;$i <= $chunk;$i++){
if($i == 1) {
$min_product_amt = min($product_amt['price']);
$index = array_search($min_product_amt, $product_amt);
$product_total = $min_product_amt;
//ceil Round numbers up to the nearest integer
$prod_total['price'] = ceil($product_total * $discount/100);
$prod_total['tax_id'] = $product_amt['tax_class_id'];
//Remove the first element from an array
array_shift($product_amt['price']);
array_shift($product_amt['tax_class_id']);
} else {
$second_min_product_amt = min($product_amt['price']);
$index = array_search($min_product_amt, $product_amt);
$product_total = $second_min_product_amt;
$prod_total['price'] = ceil($product_total * $discount/100);
$prod_total['tax_id'] = $product_amt['tax_class_id'];
array_shift($product_amt['price']);
array_shift($product_amt['tax_class_id']);
}
}
print_r($prod_total);
die;
$array=Array
(
'tax_class_id' => Array
(
0 => 12,
1 => 13,
2 => 13
),
'price' => Array
(
0 => 6233,
1 => 3195,
2 => 19192
)
);
$minValue= min($array['price']);
$minKey=array_keys($array['price'], $minValue);
$tax_id=$array['tax_class_id'][$minKey[0]];
echo $tax_id;
This code will work for your issue. First i get the minimum value of nested array price and then it's associated key. After that i just access the nested array tax_class_id and get the value of the field i need like accessing every array.
$data = [
"tax_class_id" => [
12,
13,
13
],
"price" => [
6233,
3195,
19192
]
];
$lowestFound;
foreach($data["price"] as $i => $price){
if(!$lowestFound || $lowestFound[1] > $price)
$lowestFound = [$i,$price];
}
echo $data["tax_class_id"][$lowestFound[0]];
This code get tax_class_id of lowest price key in one cycle.
I think array_column gives you a nice output.
$array=Array
(
'tax_class_id' => Array(
0 => 12,
1 => 13,
2 => 13
),
'price' => Array(
0 => 6233,
1 => 3195,
2 => 19192
)
);
// Find minimum value
$min= min($array['price']);
// Find key of min value
$Key=array_search($min, $array['price']);
// Extract all values with key[min value]
$new = array_column($array, $Key);
Var_dump($new);
The output in $new will now be
array(2) {
[0]=> int(13)
[1]=> int(3195)
}
Basically both of the values you are looking for.
https://3v4l.org/NsdiS
The first step is to create an new array with zeros. This is the code:
$amounts = [];
$row = [];
for($a = 0; $a < count($receipts_with_total); $a++){
for($b = 0; $b < count($taxes); $b++){
$row[$b] = 0;
}
$amounts[] = $row;
}
Then, i proceede to fill the array with values. The problem is, for some reason i don't know, it adds some indexs.
The code to fill the array is the next one:
//We calculate all the taxes amounts
for($i = 0; $i < count($receipts_with_total); $i++){
$this_receipt = $receipts_with_total[$i];
//We get all the taxes for each receipt
$taxes = $this_receipt->taxes;
for($j = 0; $j < count($taxes); $j++){
$this_tax = $taxes[$j];
if($this_tax->name == "IVA 21%"){
$amounts[$i][$j] = round((($this_tax->value * $total[$i]) / 100), 2);
}
elseif($this_tax->name == "IVA 10.5%"){
$amounts[$i][$j+1] = round((($this_tax->value * $total[$i]) / 100), 2);
}
else {
$amounts[$i][$j+2] = round((($this_tax->value * $total[$i]) / 100), 2);
}
}
}
And the outputs are:
Creacion
Array ( [0] => Array ( [0] => 0 [1] => 0 [2] => 0 ) [1] => Array ( [0] => 0 [1] => 0 [2] => 0 ) [2] => Array ( [0] => 0 [1] => 0 [2] => 0 ) [3] => Array ( [0] => 0 [1] => 0 [2] => 0 ) )
Modelo
Array ( [0] => Array ( [0] => 0 [1] => 257.46 [2] => 61.3 ) [1] => Array ( [0] => 0 [1] => 40.36 [2] => 9.61 ) [2] => Array ( [0] => 80.73 [1] => 40.36 [2] => 9.61 ) [3] => Array ( [0] => 211.05 [1] => 105.53 [2] => 0 ) )
Lleno
Array ( [0] => Array ( [0] => 0 [1] => 257.46 [2] => 0 [3] => 61.3 ) [1] => Array ( [0] => 0 [1] => 40.37 [2] => 0 [3] => 9.61 ) [2] => Array ( [0] => 80.73 [1] => 0 [2] => 40.37 [4] => 9.61 ) [3] => Array ( [0] => 211.05 [1] => 0 [2] => 105.53 ) )
The first output is the new array with zeros. The second one is an example of as should be the final array with the calculate numbers. The last one is the array really i get.
As you can see, the index in bold represent the errors. For example, the value "61.3" is in fourth position in the first array, instead of third, it would be the correct.
Thanks!
Remove the +1 and +2 from the code.
Just
$amounts[$i][$j]=...
in all cases.
Because if i.e.
$j=2;
it may be become 3 in your code $j+1
My answer just pick that part of your question:
The problem is, for some reason i don't know, it adds some indexs.
I guess you want to show the "IVA 21" always in the 0 index in subarray and "IVA 10.5" always in the 1 index in sub array, and so on...? So you don't have to +1 or +2 in the index...cuz $j has already been increment in the for loop...
Or if you don't know which comes first or maybe you will have more option later, do not use a for loop. Use php foreach and keep +1 manually
$j = 0;
foreach ($taxes as $$this_tax) {
if ($this_tax->name == 'IVA 21%') {
$amounts[$i][$j] = round((($this_tax->value * $total[$i]) / 100), 2);
} elseif ($this_tax->name == 'IVA 10.5%') {
$amounts[$i][$j + 1] = round((($this_tax->value * $total[$i]) / 100), 2);
} else {
$amounts[$i][$j + 2] = round((($this_tax->value * $total[$i]) / 100), 2);
}
//myabe +3 later...
}
Or why not just use a static number like 0,1,2 if you always know the length of $taxes and where you gonna put for your results. You can even create conts like:
define('IVA21', 0); // const IVA21 = 0;
define('IVA105', 1);
// ... more define
//for loop starts
if ($this_tax->name == 'IVA 21%') {
$amounts[$i][IVA21] = round((($this_tax->value * $total[$i]) / 100), 2);
}
I walk around here with some hesitation, I have passed an array with sub elements (so to speak) and I need three random values but these are obtained without repeating.
The array is as follows:
Array
(
[0] => Array
(
[uid] => 1
[ticket_code] => 0oreb8yo
)
[1] => Array
(
[uid] => 1
[ticket_code] => 2oeii8hm
)
[2] => Array
(
[uid] => 1
[ticket_code] => m0dwtjiw
)
[3] => Array
(
[uid] => 1
[ticket_code] => q6c7cymb
)
[4] => Array
(
[uid] => 1
[ticket_code] => zyqhm5bj
)
[5] => Array
(
[uid] => 1
[ticket_code] => amdqzjpi
)
[6] => Array
(
[uid] => 2
[ticket_code] => tzql7l42
)
[7] => Array
(
[uid] => 2
[ticket_code] => gap0r6vf
)
[8] => Array
(
[uid] => 2
[ticket_code] => ypqum5yz
)
[9] => Array
(
[uid] => 4
[ticket_code] => smupluac
)
[10] => Array
(
[uid] => 4
[ticket_code] => 9d8jsha7
)
[11] => Array
(
[uid] => 5
[ticket_code] => 6hdnja42
)
)
And I need you to get 3 "ticket_code" but no right to repeat the "uid".
I've been on trying as follows, but also repeats the "uid".
$ticketsWinners = array();
for ($i=0; $i < 3; $i++) {
$aux = array_rand($allTickets);
$aux2 = $allTickets[$aux]['uid'];
$ticketsWinners[] = array(
'uid' => $aux2,
'ticket_code' => $allTickets[$aux]['ticket_code']
);
}
Any way to do it without repeats?
We thank you in advance if anyone knows of something ^^
Try something like:
$ticketsWinners = array();
while (sizeof($ticketsWinners) < 3) {
$aux = array_rand($allTickets);
// array_rand return array of keys so you need first value only
$uid = $allTickets[$aux[0]]['uid']
// add uid as a key so ass not tot check all $allTickets values
if (!isset($ticketsWinners[$uid]))
$ticketsWinners[$uid] = $allTickets[$aux[0]];
}
// if you need $allTickets back to numeric keys [0, 1, 2]
$allTickets = array_values($allTickets);
if you're afraid of infinite loops (that can take place really) then try this:
$ticketsWinners = array();
// shuffle array before checking
shuffle($allTickets);
foreach ($allTickets as $tick_data) {
$uid = $tick_data['uid'];
if (!isset($ticketsWinners[$uid]))
$ticketsWinners[$uid] = $tick_data;
if (sizeof($ticketsWinners) == 3)
break;
}
Here in worst case you check $allTickets array and get winners of size <= 3.
Try this:
$ticketsWinners = array();
$ticketUid = array();
for ($i=0; $i < 3; $i++) {
$aux = array_rand($allTickets);
$aux2 = $allTickets[$aux]['uid'];
if(! in_array($aux2, $ticketUid)) {
$ticketUid[$i] = $aux2;
$ticketsWinners[] = array(
'uid' => $aux2,
'ticket_code' => $allTickets[$aux]['ticket_code']
);
} else {
$i--;
}
}
this structure would be better ( added benefit of ticket numbers being unique )
$tickets = Array
(
'0oreb8yo' => 1,
'2oeii8hm' => 1,
'm0dwtjiw' => 1,
'q6c7cymb' => 1,
'zyqhm5bj' => 1,
'amdqzjpi' => 1,
'tzql7l42' => 2,
'gap0r6vf' => 2,
'ypqum5yz' => 2,
'smupluac' => 3,
'9d8jsha7' => 4,
'6hdnja42' => 5,
);
$winners = array();
$picks = 3;
for($i = 0; $i < $picks; $i++){
if(count($tickets) == 0 ){
break; //or error -- shouldn't need this unless picks exceed uids
}
$ticket = array_rand($tickets);
$winner = $tickets[$ticket];
$winners[] = $winner;
$tickets = array_filter($tickets, function($item) use ($winner){
return $winner != $item;
});
}
echo '<pre>';
var_export($winners);
outputs
array (
0 => 2,
1 => 1,
2 => 4,
)
array (
0 => 2,
1 => 1,
2 => 3,
)
array (
0 => 1,
1 => 3,
2 => 2,
)
unlike the while option, this will reduce the operations for each loop of the for loop by reducing the ticket array by the uid. It's also the only way to insure your not always pulling out a user with tickets, what if user 1 bought 90% of the tickets, you'd loop on him 90% of the time, in any case you have to reduce the ticket array by winners if they can win only once. In essence you remove each uid from the list when they win. You can also be sure that each ticket has the same chance to win ( as well as array_rand is random that is ) - they all have equal footing.
ticket array reduction
after loop1
array (
'tzql7l42' => 2,
'gap0r6vf' => 2,
'ypqum5yz' => 2,
'smupluac' => 3,
'9d8jsha7' => 4,
'6hdnja42' => 5,
)
after loop2
array (
'smupluac' => 3,
'9d8jsha7' => 4,
'6hdnja42' => 5,
)
after loop3
array (
'smupluac' => 3,
'6hdnja42' => 5,
)
winners
array (
0 => 1,
1 => 2,
2 => 4,
)
to return both the uid and wining ticket change
$winners[] = $winner;
to
$winners[$ticket] = $tickets[$ticket];
now winners will be, just like the input array
ticketnumber => uid
ticket is the key ( which is the ticket ) and winner is the value ( which is the uid )
I can't seem to wrap my head around this.
I am given an array in PHP that looks something like this:
array (
0 => array (
0 => 50,
1 => 0.80
),
1 => array (
0 => 300,
1 => 0.50
),
2 => array (
0 => 600,
1 => 0.30
),
3 => array (
0 => 1000,
1 => 0.20
),
4 => array (
0 => 4000,
1 => 0.10
)
);
An array of arrays where the first index of the inner array represents a quantity while the second index represents a price.
I want to import this data into my database, but in a specific way.
I have specific quantities that I like to keep track of that are defined by the following array:
array(10,100,500,1000,5000,10000);
I then want to make the original array more fine tuned to quantities and prices that I would like to see. So in this particular example, I would like an array that looks like this:
array (
0 => array (
0 => 100,
1 => 0.80
),
1 => array (
0 => 500,
1 => 0.50
),
2 => array (
0 => 1000,
1 => 0.20
),
3 => array (
0 => 5000,
1 => 0.10
)
);
My new array will only contain the specific quantity indexes.
If a quantity exists in the original array, I use that price. If it doesn't exist, I would use the price of the next lowest quantity. If no lower quantity exists, I don't want to see that quantity in the new array.
I have been able to accomplish what I want for the most part with the following code:
function getRelativePrices($pricearray) {
$relativeprices = array();
$types = array(10,100,500,1000,5000,10000);
foreach ($types as $q) {
$new_array = array();
foreach ($pricearray as $index => $array) {
if ($q >= $array[0]) {
$new_array = array($q, $array[1]);
}
}
if (sizeof($new_array)) {
$relativeprices[] = $new_array;
}
}
return $relativeprices;
}
The only problem with the above is that I am getting extra data that I do not want. In the example I provided, I am getting a 5th index/array at the end that looks like:
4 => array (
0 => 10000,
1 => 0.10
)
I don't want this last piece, since I find it redundant considering that I know that 5000 pieces cost $0.10 each, so I can assume that 10000 will cost the same price when "4000" is the highest quantity given in the original array.
So I want to ask for help in removing this last piece.
Also, I was wondering if someone had a better coding method in general for converting this array.
You could just do in your inner foreach:
foreach ($pricearray as $index => $array) {
if ($q >= $array[0]) {
if($q == 10000) { continue; }
$new_array = array($q, $array[1]);
}
}
OK I think this should do the trick. I think the problem was in your comparison... See code:
function getRelativePrices($pricearray) {
$relativeprices= array();
$types = array(10,100,500,1000,5000,10000);
foreach($pricearray as $p) {
$new_array = array();
foreach($types as $t) {
if($p[0] <= $t) {
$new_array = array($t,$p[1]);
break;
}
}
if(sizeof($new_array)) {
$relativeprices[] = $new_array;
}
}
return $relativeprices;
}
Here is an example of my test based on your code examples:
function getRelativePrices($pricearray) {
$relativeprices= array();
$types = array(10,100,500,1000,5000,10000);
foreach($pricearray as $p) {
$new_array = array();
foreach($types as $t) {
if($p[0] <= $t) {
$new_array = array($t,$p[1]);
break;
}
}
if(sizeof($new_array)) {
$relativeprices[] = $new_array;
}
}
return $relativeprices;
}
$test = array (
0 => array (
0 => 50,
1 => 0.80
),
1 => array (
0 => 300,
1 => 0.50
),
2 => array (
0 => 600,
1 => 0.30
),
3 => array (
0 => 1000,
1 => 0.20
),
4 => array (
0 => 4000,
1 => 0.10
)
);
print_r(getRelativePrices($test));
And the output was:
Array
(
[0] => Array
(
[0] => 100
[1] => 0.8
)
[1] => Array
(
[0] => 500
[1] => 0.5
)
[2] => Array
(
[0] => 1000
[1] => 0.3
)
[3] => Array
(
[0] => 1000
[1] => 0.2
)
[4] => Array
(
[0] => 5000
[1] => 0.1
)
)