Milos N.
Milos N.

Reputation: 4843

How to filter php associative array by key and values?

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!

Upvotes: 0

Views: 178

Answers (4)

Evanh Shumakov
Evanh Shumakov

Reputation: 306

$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
)

Upvotes: 4

JAAulde
JAAulde

Reputation: 19560

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;
});

Upvotes: 1

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);

Upvotes: 2

Ariful Islam
Ariful Islam

Reputation: 7675

$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]) ;

Upvotes: 0

Related Questions