Преобразование массива php

384
27 декабря 2017, 21:46

Как получить из массива:

$a = [
'color' => ['red', 'blue'],
'size' => ['10-12', '12-14']
];

такой массив

$b = [
['color' => 'red', 'size' => '10-12'],
['color' => 'blue', 'size' => '10-12'],
['color' => 'red', 'size' => '12-14'],
['color' => 'blue', 'size' => '12-14'],
];

ключей и значений может быть любое кол-во,
имена ключей и значений могут быть разные

Нашел такое решение

function addValue(&$arr, $key, $val)
{
  if (!count($arr)) {
    $arr[] = [$key => $val];
    return;
  }
  foreach($arr as $num => $values) {
    $arr[$num][$key] = $val;    
  }
}
$b = [];
foreach($a as $key => $values) {
  $copyB = $b;
  $newB = [];
  foreach($values as $value) {
    $currentB = $copyB;
    addValue($currentB, $key, $value);
    $newB = array_merge($newB, $currentB);
  }
  $b = $newB;
}
Answer 1

Исходные данные

$a = [
       'color' => ['red', 'blue'],
       'size'  => ['10-12', '12-14', '15-16'],
       // еще элементы  
   ];

генерим структуру для перестановок

foreach(array_keys($a) as $idx => $key){
    $data[$idx] = ['key' => $key, 'c' => count($a[$key]), 'v' => 0];
}

где каждый элемент содержит ключ массива $a, число возможных элементов в нем, и текущее значение в перестановке.

далее в виде рекурсивной процедуры генерация перестановок, на последнем уровне рекурсии пополняем массив рузультатов по текущей перестановке:

function gen(&$data, $s = 0){
    if($s == count($data)){
        global $a;
        global $result;
        $v = [];
        foreach($data as $x){
            $v[$x['key']] = $a[$x['key']][$x['v']];
         }
        $result[] = $v;
        return;
    }
    for($i=0; $i < $data[$s]['c']; $i++){
        $data[$s]['v'] = $i;
        gen($data, $s+1);
    }
}

запускаем

$result = [];
gen($data);
print_r($result);

результат

Array (
    [0] => Array (
            [color] => red
            [size] => 10-12
        )
    [1] => Array (
            [color] => red
            [size] => 12-14
        )
    [2] => Array (
            [color] => red
            [size] => 15-16
        )
    [3] => Array (
            [color] => blue
            [size] => 10-12
        )
    [4] => Array (
            [color] => blue
            [size] => 12-14
        )
    [5] => Array (
            [color] => blue
            [size] => 15-16
        )
)   

пример с другими исходными данными

$a = [
       'color' => ['red', 'blue'],
       'size' => ['10-12'],
       'weight' => ['bold', 'normal'],
   ];

результат

Array   (
    [0] => Array            (
            [color] => red
            [size] => 10-12
            [weight] => bold
        )
    [1] => Array            (
            [color] => red
            [size] => 10-12
            [weight] => normal
        )
    [2] => Array            (
            [color] => blue
            [size] => 10-12
            [weight] => bold
        )
    [3] => Array            (
            [color] => blue
            [size] => 10-12
            [weight] => normal
        )
)
Answer 2

array_map

$array = [
    'color' => ['red', 'blue'],
    'size' => ['10-12', '12-14']
];
function arrayMapping($a, $b){
    return [
        "color" => $a,
        "size"  => $b
    ];
}
$output_array = array_map("arrayMapping", $array['color'], $array['size']);
/*
Array ( 
    [0] => Array ( [color] => red [size] => 10-12 ) 
    [1] => Array ( [color] => blue [size] => 12-14 ) 
)
*/
Answer 3

нашел такое решение, как вам

function cartesian($input) {
    $result = array();
    while (list($key, $values) = each($input)) {
        // If a sub-array is empty, it doesn't affect the cartesian product
        if (empty($values)) {
            continue;
        }
        // Seeding the product array with the values from the first sub-array
        if (empty($result)) {
            foreach($values as $value) {
                $result[] = array($key => $value);
            }
        }
        else {
            // Second and subsequent input sub-arrays work like this:
            //   1. In each existing array inside $product, add an item with
            //      key == $key and value == first item in input sub-array
            //   2. Then, for each remaining item in current input sub-array,
            //      add a copy of each existing array inside $product with
            //      key == $key and value == first item of input sub-array
            // Store all items to be added to $product here; adding them
            // inside the foreach will result in an infinite loop
            $append = array();
            foreach($result as &$product) {
                // Do step 1 above. array_shift is not the most efficient, but
                // it allows us to iterate over the rest of the items with a
                // simple foreach, making the code short and easy to read.
                $product[$key] = array_shift($values);
                // $product is by reference (that's why the key we added above
                // will appear in the end result), so make a copy of it here
                $copy = $product;
                // Do step 2 above.
                foreach($values as $item) {
                    $copy[$key] = $item;
                    $append[] = $copy;
                }
                // Undo the side effecst of array_shift
                array_unshift($values, $product[$key]);
            }
            // Out of the foreach, we can add to $results now
            $result = array_merge($result, $append);
        }
    }
    return $result;
}
Answer 4

Уже предложено достаточно решений. Поэтому добавлю свой вариант без рекурсий. Да ещё и на генераторе!

$in = [
    'a' => ['11', '12'],
    'b' => ['21', '22'],
];
function decart($in)
{
    $keys = array_keys($in);
    // тут будем хранить индексы подходящих значений
    $offset = array_fill_keys($keys, 0);
    // стартуем генератор
    while (true) {
        // собираем очередной набор значений по их индексам
        $arr = [];
        foreach ($keys as $i => $key) {
            $arr[] = $in[$key][$offset[$key]];
        }
        // и возвращаем набор
        yield $arr;
        // всегда увеличивем индекс значений первого вложенного массива
        $offset[$keys[0]]++;
        // если вышли за его границы, то ..
        if ($offset[$keys[0]] === count($in[$keys[0]])) {
            // .. снова будем перебирать значения с первого, но ..
            $offset[$keys[0]] = 0;
            // .. увеличиваем индекс следующего массива в котором это возможно
            for ($i = 1; $i < count($keys); $i++) {
                $offset[$keys[$i]]++;
                // удалось и нет переполнения? - можем вернуться в генератор и ждать запроса
                if ($offset[$keys[$i]] < count($in[$keys[$i]])) {
                    break;
                }
                // в случае переполнения сбрасываем индекс и для этого массива, -
                // - очередная итерация увеличит следующий по порядку
                $offset[$keys[$i]] = 0;
                // кроме случая, когда достигнут конец исходного массива - останавливаем генератор
                if ($i === count($keys) - 1) {
                    break 2;
                }
            }
        }
    }
}
$keys = array_keys($in);
$generator = decart($in);
$out = [];
foreach ($generator as $values) {
    $out[] = array_combine($keys, $values);
}
var_dump($out);

В итоге имеем декартово произведение значений:

array(4) {
  [0]=>array(2) {
    ["a"]=>string(2) "11"
    ["b"]=>string(2) "21"
  }
  [1]=>array(2) {
    ["a"]=>string(2) "12"
    ["b"]=>string(2) "21"
  }
  [2]=>array(2) {
    ["a"]=>string(2) "11"
    ["b"]=>string(2) "22"
  }
  [3]=>array(2) {
    ["a"]=>string(2) "12"
    ["b"]=>string(2) "22"
  }
}
Answer 5

Задача становится проще, если её разделить на небольшие части. Для начала бывает полезно сформулировать проблему более конкретно. В данном случае можно сказать, что мы хотим получить все возможные комбинации параметров и при этом не потерять их имена. Начнём с комбинаций. Это проще, если комбинировать только два набора значений. Можно написать функцию типа такой:

function arr_comb($a, $b) {
    $combs = [];
    foreach ($a as $aVal) {
        foreach ($b as $bVal) {
            $combs[] = [$aVal, $bVal];
        }
    }
    return $combs;
}

Очень просто! Теперь попробуем собрать комбинации трёх наборов значений. А это комбинация третьего с результатами комбинации первых двух. А четырёх? Рекурсия напрашивается сама :-)

function arr_comb(...$arrs) {
    $size = count($arrs);
    if ($size === 2) {
        list($a, $b) = $arrs;
        $combs = [];
        foreach ($a as $aVal) {
            foreach ($b as $bVal) {
                $combs[] = [$aVal, $bVal];
            }
        }
        return $combs;
    } elseif ($size > 2) {
        $last = array_pop($arrs);
        return arr_comb(arr_comb(...$arrs), $last);
    } else {
        return $arrs;
    }
}

Однако, мы комбинируем результаты-массивы со значениями и на выходе получаем вложенные массивы. Надо сделать их плоскими, например так:

elseif ($size > 2) {
    $last = array_pop($arrs);
    $combs = arr_comb(arr_comb(...$arrs), $last);
    return array_map(function($combo) {
        $flatArr = array_values($combo[0]);
        $flatArr[] = $combo[1];
        return $flatArr;
    }, $combs);
}

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

function build($arr) {
    $combs = arr_comb(...array_values($arr)); // все комбинации
    $keys = array_keys($arr); // имена параметров
    return array_map(function($vals) use($keys) {
        return array_combine($keys, $vals);
    }, $combs);
}
Answer 6
function cross($array)
{
    $result     = [];
    $current    = array_splice($array, -1);
    foreach ($current as $key => $values) {
        foreach ( $values as $value) {
            if (empty($array)) {
                array_push($result, [$key => $value]);
            } else {
                foreach (cross($array) as $temp) {
                    array_push($result, array_merge([$key => $value], $temp));
                }
            }
        }
    }
    return $result;
}
print_r(cross($a));

так лучше

READ ALSO
В чём различия Pretty Url?

В чём различия Pretty Url?

Есть два вида pretty url:

180
настройка в netbeans + openserver xdebug

настройка в netbeans + openserver xdebug

Установил netbeans 82 + openserver 5

224
привести массив к нужной форме

привести массив к нужной форме

У меня на input весит обработчик с отправкой AJAX на сервер вот такого вида

182
return возвращает NULL

return возвращает NULL

Почему функция возвращает NULL? Если сделать дамп в функции перед словом return тогда выводится ожидаемый масив, при дампе функции tree_menu() - NULL

165