Найдите ключ max и min в ассоциативном массиве

У меня есть ассоциативный массив вроде

array
{
    [company 1]=>array
                      (
                        [1981] => 1
                        [1945] => 3
                      )
   [company 2]=>array
                    (
                       [1990] => 18
                       [2005] => 13
                    )
   [company 3]=>array
                    (
                       [1950] => 6
                       [2012] => 9
                    )
}

Я хочу получить самый низкий и самый высокий ключ, т.е. 1945 и 2012 годы. Как я могу этого добиться? Я уже искал stackoverflow, и самое высокое значение ассоциативного массива является ближайшим возможность, но он выдает минимальное и максимальное значение, и мне нужны минимальные и максимальные ключи.

** Я не хочу использовать цикл foreach **


person Deadlock    schedule 15.11.2012    source источник
comment
Я не хочу использовать цикл foreach, в чем дело с этим произвольным требованием?   -  person sachleen    schedule 15.11.2012
comment
Мой результирующий массив очень большой, поэтому он занимает много времени при рекурсии и создании нового массива с использованием foreach. В то время как, если бы это можно было сделать с помощью функций min или max, это было бы действительно полезно.   -  person Deadlock    schedule 15.11.2012
comment
-1 за произвольные ограничения, запрещающие сам инструмент, созданный для подобных задач. Фактически, требование ставит этот вопрос прямо на пересечение слишком локализованного и неконструктивного.   -  person cHao    schedule 15.11.2012
comment
@cHao Я думал, что это можно сделать с помощью функции array_keys, min, max, array_map ... Я просто не знаю, как ... вот почему я спросил здесь ... и я уже сказал, почему нужно избегать использования foreach в предыдущем сообщении ..   -  person Deadlock    schedule 15.11.2012
comment
Решение этого вопроса не имеет ничего общего с построением нового массива даже при использовании foreach. Таким образом, запрет, скорее всего, ошибочное предположение. Да, кстати, даже при использовании других функций ... Как вы думаете, не будет необходимости в цикле php внутри массива, если вы этого не заметите?   -  person Andrius Naruševičius    schedule 15.11.2012
comment
@Deadlock: Ваша причина неверна. Как бы то ни было, вам придется перебирать весь массив (и каждый массив внутри, если только вы не отсортировали подмассивы). Ничего из этого не требует создания новых массивов. Однако забавная часть: если вы используете array_keys или array_map, вы все равно создаете новый массив.   -  person cHao    schedule 15.11.2012


Ответы (4)


Если вы действительно ненавидите foreach, вот решение:

$arr = array(
  "Company 1" => array(
    "1981" => 1,
    "1945" => 3
  ),

  "Company 2" => array(
    "1990" => 18,
    "2005" => 13
  ),

  "Company 3" => array(
    "1950" => 6,
    "2012" => 9
  )
);


$arr = array_map("array_keys", $arr);
$arr = array_reduce($arr, "array_merge", array());

Ваш $arr будет таким:

Array
(
    [0] => 1981
    [1] => 1945
    [2] => 1990
    [3] => 2005
    [4] => 1950
    [5] => 2012
)

Теперь вы можете использовать функции min() и max() или sort() легко получить максимальное и минимальное значение.

sort($arr);
echo end($arr); /*highest value; actual output: 2012*/
echo reset($arr); /*lowest value; actual output: 1945*/
person Passerby    schedule 15.11.2012

Попробуйте это:
используя foreach.

        $array = array("company 1" => array(1981 => 1, 1945 =>3),
                        "company 2" => array(1990 => 18, 2005 => 13),
                        "company 3" => array(1950 => 6, 2012 =>9),
        );

        $keys = array();
        foreach($array as $arr)
        {
            foreach( array_keys($arr) as $val)
            {
                array_push($keys, $val);
            }
        }
        sort($keys);
        $min = $keys[0];
        $max = $keys[count($keys)-1];

Без foreach:

        global $keys;
        $GLOBALS['keys'] = array();
        function sortme($arr)
        {
            is_array($arr)? array_map("sortme",array_keys($arr)): array_push($GLOBALS['keys'], $arr);

        }
        array_map("sortme",$array);
        sort($GLOBALS['keys']);
        $min = $GLOBALS['keys'][0];
        $max = $GLOBALS['keys'][count($GLOBALS['keys'])-1];
        echo "min = ".$min . "<br/>max = ".$max;
person sephoy08    schedule 15.11.2012
comment
Я уже упоминал в вопросе, что не хочу использовать цикл foreach. - person Deadlock; 15.11.2012

ksort($array);
$min = reset($array);
end($array);
$max = key($array);

РЕДАКТИРОВАТЬ: это работает для простого массива. У вас двухуровневая структура, поэтому было бы почти невозможно избежать зацикливания на ней.

Однако, если у вас так много записей, что даже foreach работает слишком медленно, вероятно, вам следует пересмотреть свой подход. Например, поместите этот список в базу данных и используйте SQL, чтобы сделать за вас тяжелую работу.

Как именно? Установите экземпляр MySQL или PostgreSQL (я лично предпочитаю Postgres по многим причинам), создайте базу данных, создайте таблицу с такой структурой:

CREATE TABLE mytable (
    mytable_id INTEGER PRIMARY KEY,
    company VARCHAR(16),
    year INTEGER,
    value INTEGER,
    -- put some more metadata...
)

Технически вам следует нормализовать свою базу данных и создать отдельные таблицы для каждого объекта (например, таблицы для компаний, клиентов, заказов и т. Д.), Но вы можете вернуться к этому позже.

Создайте индексы для столбцов, по которым вы собираетесь искать (например, год, компания и т. Д.):

CREATE INDEX mytable_year_idx ON mytable (year);
...

Наконец, в своем PHP-скрипте подключитесь к базе данных и запросите у нее то, что вы хотели, примерно так:

SELECT min(year) AS min_year,
       max(year) AS max_year
FROM mytable
person mvp    schedule 15.11.2012
comment
Это работает для простого массива. У вас двухуровневая структура. Вам все равно придется перебирать компании, а затем для каждой записи компании вы можете использовать эту процедуру, чтобы получить локальные минимальные и максимальные значения. - person mvp; 15.11.2012
comment
Если у вас так много записей, что даже foreach работает слишком медленно, вероятно, вам следует пересмотреть свой подход. Например, поместите этот список в базу данных и используйте SQL, чтобы сделать за вас тяжелую работу. - person mvp; 15.11.2012

Поскольку ваш входной массив «очень большой» и «занимает [много] много времени», это как раз оправдание использования набора простых языковых конструкций для итерации набора данных за один проход.

Использование нескольких функций массива может выглядеть более лаконичным или умным, но они будут выполнять несколько проходов по данным и неизменно потреблять больше ресурсов, чем это абсолютно необходимо.

Поскольку ваши данные имеют дело с годами, вы можете встроить бизнес-логику в логику кода с помощью «магических чисел» - предполагая, что вы никогда не столкнетесь с отрицательным годом или годом из 10 000 и более.

Если вам нужен более независимый от бизнеса подход, вы можете заполнить минимальные и максимальные значения по умолчанию, сдвинув первую строку из входного массива и получив ключи min и max этого подмассива.

Код магического числа: (Демо)

$minYear = 9999;
$maxYear = 0;
foreach ($array as $row) {
    foreach ($row as $year => $value) {
        if ($year > $maxYear) {
            $maxYear = $year;
        }
        if ($year < $minYear) {
            $minYear = $year;
        }
    }
}
echo "MinYear = $minYear, MaxYear = $maxYear";
// MinYear = 1945, MaxYear = 2012

Измененный код значений по умолчанию: (Демо)

if ($array) {
    $firstKeys = array_keys(array_shift($array));
    $minYear = min($firstKeys);
    $maxYear = max($firstKeys);
} else {
    $minYear = null;
    $maxYear = null;
}
foreach ($array as $row) {
    foreach ($row as $year => $value) {
        if ($year > $maxYear) {
            $maxYear = $year;
        } elseif ($year < $minYear) {
            $minYear = $year;
        }
    }
}
echo "MinYear = $minYear, MaxYear = $maxYear";

Обратите внимание, что фрагмент Shifted специально обрабатывает первый набор данных для получения значений по умолчанию, что требует нескольких дополнительных строк, но имеет более высокую потенциальную эффективность из-за elseif.

Ни один из этих методов не использует повторные вызовы функций, поэтому практически гарантировано, что они работают быстрее, чем функциональные методы.

person mickmackusa    schedule 12.03.2020