0

I have this data:

Array
(
    [0] => Array
        (
            [name] => Hair Transplantation
            [price] => € 1000 - 4000
        )

    [1] => Array
        (
            [name] => Rhinoplasty
            [price] => € 2500
        )

    [2] => Array
        (
            [name] => Otoplasty
            [price] => € 1000
        )

)

I want to filter this array by price key and, as a result, I want to be returned only one item, item with lowest price number.

Thank you!

10
  • I guess the first item has a range of prices? How should that sort compared to items with single prices? Commented Aug 6, 2020 at 18:58
  • The price here is a string and not an integer. Any reason why you have it so? Commented Aug 6, 2020 at 19:00
  • @Don'tPanic, hi, thank you for your response. Yes. The first item (in this case) have range, get the lowest item from that range and filter that number with other single prices. Commented Aug 6, 2020 at 19:01
  • @vivek_23, yes that is a string with "€" sign, I want to remove that sign and return the only number who will be filtered. Commented Aug 6, 2020 at 19:02
  • 2
    This can be done, but it will require some unreliable string parsing--especially around the ranges. You should really look at fixing this data structure Commented Aug 6, 2020 at 19:03

4 Answers 4

4
$data = [/* your data */];
uasort($data, function($a, $b) {
    preg_match("/\d+/", $a['price'], $matchesA);
    preg_match("/\d+/", $b['price'], $matchesB);
    return (int)$matchesB[0] <=> (int)$matchesA[0];
});

$result = array_pop($data);

Added (08.08.2020) Because of vivek_23's comment, I added second solution:

    $result = array_reduce($price, 'getMinimumPrice', null);

    function getMinimumPrice(?array $itemA, array $itemB): array
    {
        if (is_null($itemA)) {
            return $itemB;
        }
        return getPrice($itemA) < getPrice($itemB)
            ? $itemA
            : $itemB;
    }
    
    function getPrice(array $item): int
    {
        preg_match("/\d+/", $item['price'], $matches);
        return $matches[0];
    }

And also I check the speed difference:

$primary = [
    [
        'name' => 'Hair Transplantation',
        'price' => '€ 1000 - 4000',
    ],
    [
        'name' => 'Rhinoplasty',
        'price' => '€ 2500',
    ],
    [
        'name' => 'Otoplasty',
        'price' => '€ 1000',
    ],
    /* ... 155 items */
];

function getMinimumPrice(?array $itemA, array $itemB): array
{
    if (is_null($itemA)) {
        return $itemB;
    }
    return getPrice($itemA) < getPrice($itemB)
        ? $itemA
        : $itemB;
}

function getPrice(array $item): int
{
    preg_match("/\d+/", $item['price'], $matches);
    return $matches[0];
}

$timeRepeat = 1000;
$reduce = 0;
for ($i = 0; $i < $timeRepeat; $i++) {
    $start = microtime(true);
    $price = $primary;
    array_reduce($price, 'getMinimumPrice', null);
    $reduce += microtime(true) - $start;
}

$uasort = 0;
for ($i = 0; $i < $timeRepeat; $i++) {
    $start = microtime(true);
    $price = $primary;
    uasort($price, function($a, $b) {
        preg_match("/\d+/", $a['price'], $matchesA);
        preg_match("/\d+/", $b['price'], $matchesB);
        return (int)$matchesB[0] <=> (int)$matchesA[0];
    });
    array_pop($price);
    $uasort += microtime(true) - $start;
}

print_r([
    'uasort' => $uasort,
    'reduce' => $reduce,
    'difference' => round($uasort / $reduce, 12),
]);

My results:

Array (
       [uasort] => 8.0096476078033
       [reduce] => 2.1610336303711
       [difference] => 3.706396557294
)
Sign up to request clarification or add additional context in comments.

5 Comments

Clever and short. It does assume that if there's a range given, the lowest is written first (left to right). I think it's a safe assumption, though. Nice work!
( plus 1 from me) :)
Could replace all lines in uasort with return (int)trim($a['price'], "€") <=> (int)trim($b['price'], "€");
I don't support sorting if all you need is just the min. Sorting is too expensive.
@vivek_23, I bet that it is nothing compare with input/output cost. Your worries are like getting couple Big Macs and Coke Zero : )
2

Try this :

$items is your base array.

$items = [
    0 => [
        "name" => "Hair Transplantation",
        "price" => "€ 1000 - 4000"
    ],

    1 => [
        "name" => "Rhinoplasty",
        "price" => "€ 2500"
    ],
    2 => [
        "name" => "Otoplasty",
        "price" => "€ 1000"
    ]
];

$lowestPrice = PHP_INT_MAX;
$rightKey = -1;

foreach ( $items as $key => $item )
{
    $clean = str_replace('€ ', '', $item['price']);
    $chunks = explode('-', $clean);

    if ( count($chunks) == 2 )
        $price = floatval(trim($chunks[0]));
    else
        $price = floatval(trim($clean));

    if ( $price < $lowestPrice )
    {
        $lowestPrice = $price;
        $rightKey = $key;
    }
}

$lowestItem = $items[$rightKey];

print_r($lowestItem);

EDIT: Regex version

$items = [
    0 => [
        "name" => "Hair Transplantation",
        "price" => "€ 1000 - 4000"
    ],

    1 => [
        "name" => "Rhinoplasty",
        "price" => "€ 2500"
    ],
    2 => [
        "name" => "Otoplasty",
        "price" => "€ 1000"
    ]
];

$lowestPrice = PHP_INT_MAX;
$rightKey = -1;

foreach ( $items as $key => $item )
{
    $matches = [];

    if ( !preg_match('#([0-9\.]+) #', str_replace(',', '.', $item['price']), $matches) )
        continue;

    $price = floatval($matches[0]);

    if ( $price < $lowestPrice )
    {
        $lowestPrice = $price;
        $rightKey = $key;
    }
}

$lowestItem = $items[$rightKey];

print_r($lowestItem);

5 Comments

This gets it done, but it's a little spread out and "knows too much" about the format of the price string. Given how unideal string parsing is, the less the code needs to know about the format, the better. (Admittedly, the regexes in my answer will blow up if decimals or commas appear in the numbers, but we could pre-plan for that). Good work, though! +1
I made some fixes, write down in one shot ... But If you are unsure, clearly, replace the research by a regex that get the first full number appearing in item['line']
Here it is with a regex
I removed 999999 for PHP_INT_MAX, more explicit about the intent.
... and improved the way of taking care of comma or point value.
1

I don't like the data structure, as I noted in the comments on the question. It leads to string parsing which is not ideal.

I accomplished it, however, using

The following code will produce your desired result in the variable, $lowest_item. If any of them have the same price (or same starting price), the first one that was defined will be the one returned (it could be edited to adjust which gets preference, though).

<?php

$data = [
    [
        'name' => 'Hair Transplantation',
        'price' => '€ 1000 - 4000',
    ],
    [
        'name' => 'Rhinoplasty',
        'price' => '€ 2500',
    ],
    [
        'name' => 'Otoplasty',
        'price' => '€ 1000',
    ],
];


$lowest_item = array_reduce($data, function ($carry, $item) {
    $return_to_next = null;

    if (!$carry) {
        $return_to_next = $item;
    } else if ($carry['price'] === $item['price']) {
        $return_to_next = $carry;
    } else {
        preg_match_all('/\d+/', $carry['price'], $carry_prices, PREG_PATTERN_ORDER);
        preg_match_all('/\d+/', $item['price'], $item_prices, PREG_PATTERN_ORDER);

        $carry_compare = min($carry_prices[0]);
        $item_compare = min($item_prices[0]);

        if ($carry_compare <= $item_compare) {
            $return_to_next = $carry;
        } else {
            $return_to_next = $item;
        }
    }

    return $return_to_next;
});

Comments

0
$people = array(
    0 => array(
      'name' => 'Hair Transplantation',
      'price' => '€ 1000 - 4000'
    ),
    1=> array(
      'name' => 'Rhinoplasty',
      'price' => '€ 2500'
    ),
    2=> array(
        'name' => 'Otoplasty',
        'price' => '€ 1000'
    )
);

$price = array_map(function($value) {
    if (strpos($value, '-') !== false) {
        $value = explode("-", $value)[0];
    }
    list($symbol, $price) = sscanf($value,'%[^0-9]%s');
    return intval($price);
}, array_column($people, 'price'));

$found_key = array_search(min($price),$price);

print_r($people[$found_key]) ;

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.