0

I need to create a unique value for $total, to be different from all other values from received object. It should compare total with order_amount from object, and then if it is the same, it should increase its value by 0.00000001, and then check again through that object to see if it matches again with another order_amount. The end result should be a unique value, with minimal increase compared to the starting $total value. All values are set to have 8 decmal places.

I have tried with the following but it won't get me the result i need. What am i doing wrong?

function unique_amount($amount, $rate) {

    $total = round($amount / $rate, 8);
    $other_amounts = some object...;

    foreach($other_amounts as $amount) {
        if ($amount->order_amount == $total) {
            $total = $total + 0.00000001;
        }
    }

    return $total;
}
14
  • What version of PHP are you using? (php -v). There are known bugs with PHP and decimals around 5.3 or 5.4. Just one tip: $total += 0.00000001 is the same as $total = $total + 0.00000001; Further more, you can "type cast" the $total as float, and at should help with: settype($foo, "float"); You can also set the decimal percision: number_format($number, X, '.', ''); where X is the number of decimals place you expect. Commented Aug 22, 2018 at 19:01
  • It isn't clear what you are trying to do here. Anyway, this would fail if you have $other_amounts = [4.00000001,4] and $total = 4. For now, just consider $other_amounts as an array of floats. Commented Aug 22, 2018 at 19:03
  • PHP is version 7. @vivek_23 The amounts must match precisely, and they all have 8 decimal places. I am trying to create a unique value, not to match the values from object, but to have a minimal increase compared to the original value of $total. Commented Aug 22, 2018 at 19:05
  • @user2406735 You didn't get the point. When you find a match and do $total = $total + 0.00000001; , it is possible that this new $total was previously found but just didn't have a match because $total was different initially. Commented Aug 22, 2018 at 19:10
  • @vivek_23 If values match, it should get the new total and compare it again with the same object. I have tried creating it as a function, and then break the foreach to runt the function again with new total, but wasn't able to get the result. Do you understand what i want, and are you able to help? Thanks Commented Aug 22, 2018 at 19:13

2 Answers 2

1
<?php

define('EPSILON',0.00000001);
$total = 4.00000000;
$other_amounts = [4.00000001,4.00000000,4.00000002];

sort($other_amounts);

foreach($other_amounts as $each_amount){
    if($total === $each_amount){ // $total === $each_amount->order_amount , incase of objects
        $total += EPSILON;
    }
}

var_dump($total);

OUTPUT

float(4.00000003)

You may add an additional break if $total < $each_amount to make it a bit more efficient.

UPDATE

To sort objects in $other_amounts based on amount, you can use usort.

usort($other_amounts,function($o1,$o2){
    if($o1->order_amount < $o2->order_amount ) return -1;
    else if($o1->order_amount > $o2->order_amount ) return 1;
    return 0;
});
Sign up to request clarification or add additional context in comments.

10 Comments

What if $other_amounts = [1.00000000,4.00000001,4.00000000,4.00000002]; I would still need 4.00000003 but in this case it will start from 1?
@user2406735 The answer will still be 4.00000003 for [1.00000000,4.00000001,4.00000000,4.00000002]. You may run it and check.
a removal of values bellow $total would possibly work then
@user2406735 You may have to change sort() function a bit since $other_amounts is a collection of objects in your case.
Thanks for your answer. After further looking into the direction you pointed, the problem was in the comparison of floats. Thanks again!
|
0

Ok, here's the solution I came up with. First I created a function to deliver random objects with random totals so I could work with, unnecessary for you but useful for the sake of this test:

function generate_objects()
{
    $outputObjects = [];

    for ($i=0; $i < 100; $i++) {
        $object = new \stdClass();

        $mainValue = random_int(1,9);
        $decimalValue = random_int(1,9);

        $object->order_amount = "{$mainValue}.0000000{$decimalValue}";

        $outputObjects[] = $object;
    }

    return $outputObjects;
}

And now for the solution part, first the code, then the explanation:

function unique_amount($amount, $rate) {
    $total = number_format(round($amount / $rate, 8), 4);

    $searchTotal = $total;
    if (strpos((string) $searchTotal, '.') !== false) {
        $searchTotal = str_replace('.', '\.', $searchTotal);
    }

    $other_amounts = generate_objects();

    $similarTotals = [];
    foreach($other_amounts as $amount) {
        if (preg_match("/^$searchTotal/", $amount->order_amount)) {
            $similarTotals[] = $amount->order_amount;
        }
    }

    if (!empty($similarTotals)) {
        rsort($similarTotals);

        $total = ($similarTotals[0] + 0.00000001);
    }

    // DEBUG
    //echo '<pre>';
    //$vars = get_defined_vars();
    //unset($vars['other_amounts']);
    //print_r($vars);
    //die;

    return $total;
}

$test = unique_amount(8,1);

echo $test;

I decided to use RegEx to find the amounts that starts with the ones I provided. Since in the exercise I provided only integers with 1-9 in the last decimal case, I tracked them and added them to one array $similarTotals.

Then I sorted this array, if not empty, descending by the values, got the first item and incremented by 0.00000001.

So, finally, returning either the $total (assuming nothing was found) or the incremented first item in the array.

PS. I did not expect this code to be this big but, well...

You can see the test here: https://3v4l.org/WGThI

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.