如何通过键连接两个关联数组,以产生另一个关联数组?

时间:2016-05-12 20:58:15

标签: php

$arr = array( array( "one" => 1, "two-two" => 2, "four" => 4),
            array( "two-two" => 22, "three" => 33, "four" => 44));

$keys = array_flip( array_keys( call_user_func_array( 'array_merge', $arr ) ) );
array_walk( $keys, function( &$val, $key ){ $val = ucwords( str_replace( array( "_", "-" ), " ", $key ) ); } );
print_r( $keys );

结果: Array ( [one] => One [two-two] => Two Two [four] => Four [three] => Three )

代码:

  1. 展平并合并二维数组,因此只有唯一的键
  2. 翻转array_keys,以便键再次出现在应有的位置(键,而不是值)
  3. 将每个元素的值设置为字符串转换后的键版本(大写,并用空格替换短划线和下划线)。
  4. 我觉得这里应该有一个单行,不能看到它。你能吗?

4 个答案:

答案 0 :(得分:1)

好吧,你可以使用array_walk_recursive。虽然它不是单行,但你用这个函数调用较少的函数。

$arr = array( array( "one" => 1, "two-two" => 2, "four" => 4),
        array( "two-two" => 22, "three" => 33, "four" => 44));

$res = array();
array_walk_recursive($arr, function ($val, $key) use (&$res) {
    $res[$key] = ucwords(str_replace(array('_', '-'), ' ', $key));
});

答案 1 :(得分:1)

从功能程序员的角度来看

我将通过几次代码迭代,并解释我一直在采取的步骤。我的大多数决定都源于始终降低复杂性的需要。例如,当您使用array_walk时,您是否必须考虑如何通过数组进行迭代?您是否考虑过数组索引或确保在每次迭代后增加?不,array_walk是强大的,因为它隐藏那些令人讨厌的细节远离程序员 - 但是这些事情不会让我们担心。

那你为什么要担心这样的烦恼?

ucwords( str_replace( array( "_", "-" ), " ", $key ) )

这是它自己的功能,我们称之为humanIdentifier。它需要一些程序化的$key值并返回一个漂亮的,人性化的字符串

function humanIdentifier ($x) {
  return ucwords(str_replace('-', ' ', $x));
}

通过这一个小小的改变,我们将已经简化了你的代码的很多的 - 复杂性下降,因为我们不再需要担心的如何的做钥匙到人类可读的字符串转换。那些令人讨厌的细节已被抽象掉了。

// things are improving ...
array_walk( $keys, function( &$val, $key ){ $val = humanIdentifier($val); } );

这是我将采取的方法,因为我继续研究这个答案的其他部分

array_map糟透了

许多功能性居民(例如)array_reducearray_maparray_filterarray_walk都是PHP中的灾难。接口是非常不一致的,行为有时只是简直不可思议。正如您和其他人所指出的那样,array_map并没有为我们提供访问的方法,但是没有什么可以阻止您制作一个可以访问它的通用函数。< / p>

function array_kmap (callable $f, iterable $xs) {
  return array_reduce(array_keys($xs), function ($acc, $k) use ($xs, $f) {
    return array_merge($acc, [$k => call_user_func($f, $k, $xs[$k], $xs)]);
  }, []);
}

结合您的humanIdentifier功能,我们可以很容易地找到解决方案

$a = [
  ['one' => 1, 'two-two' => 2, 'four' => 4],
  ['two-two' => 22, 'three' => 33, 'four' => 44]
];

$b = array_reduce($a, function ($acc, $x) {
  return array_merge($acc, array_kmap(function ($k, $v) {
    // we don't actually use `$v`, so we can ignore it
    return humanIdentifier($k);
  }, $x));
}, []);

print_r($b);
// Array
// (
//     [one] => One
//     [two-two] => Two Two
//     [four] => Four
//     [three] => Three
// )

隐藏的复杂性

$a$b的转换中仍然隐藏着一些复杂性。你能发现它吗? array_reduce几乎是用于迭代的大多数功能的祖父 - 它非常强大,但它必须将这种功能归功于其极其通用的界面。我们对array_reduce的使用几乎是array_merge和我们的映射函数$f的包装。我们可以推导出一个新功能,它可以作为一种专门的array_reduce - 大多数函数式语言称之为平面地图

function array_flatmap (callable $f, iterable $xs) {
  return array_reduce(array_map($f, $xs), 'array_merge', []);
}

$a = [
  ['one' => 1, 'two-two' => 2, 'four' => 4],
  ['two-two' => 22, 'three' => 33, 'four' => 44]
];

$b = array_flatmap(function ($x) {
  return array_kmap(function ($k) {
    return humanIdentifier($k);
  }, $x);
}, $a);

print_r($b);
// Array
// (
//     [one] => One
//     [two-two] => Two Two
//     [four] => Four
//     [three] => Three
// )

转换

什么? Eta conversion来自lambda演算,并说

 function ($x) { return $f($x); }      === $f

(function ($x) { return $f($x); })($y) === $f($y)
                        $f($y)         === $f($y)
                        $f             === $f

我提到这一点是因为我们可以通过eta转换一些代码来降低更多的复杂性。有两个eta转换可以帮助我们的程序。你看到了哪里?

array_kmap(function ($k) {
  return humanIdentifier($k);
}, $x)

这个悬空$k可以轻松删除 - 这里是简化但等效的代码(注意:这是可能的,因为我们从您的可调用中丢弃$v值 - 只需要密钥计算我们的转型)

array_kmap('humanIdentifier', $x)

现在,如果我们稍微缩小一下,我们会看到这个!

function ($x) {
  return array_kmap('humanIdentifier', $x);
}

我们的$x函数末尾的另一个小悬空array_kmap!如果我们partially apply我们的array_kmap功能,我们可以删除$x point这将摆脱function ($x) { ... }完全。

当然PHP没有任何部分应用函数的方法,所以我们必须做到这一点

function partial (callable $f, ...$xs) {
  return function (...$ys) use ($f, $xs) {
    return call_user_func($f, ...$xs, ...$ys);
  };
}

现在我们的结果转变是一件美丽的事情

$a = [
  ['one' => 1, 'two-two' => 2, 'four' => 4],
  ['two-two' => 22, 'three' => 33, 'four' => 44]
];

$b = array_flatmap(partial('array_kmap', 'humanIdentifier'), $a);

print_r($b);
// Array
// (
//     [one] => One
//     [two-two] => Two Two
//     [four] => Four
//     [three] => Three
// )

代码反映数据反映代码

...数据反映代码反映数据......看看我们最后的代码:

$b = array_flatmap(partial('array_kmap', 'humanIdentifier'), $a);

我们正在做一个array_map的array_map - 这是有道理的,因为我们的初始数据是一个数组数组!在这里,我们的代码设计反映了它所运行的数据的形状。

这很好,因为即使我们没有写这个,我们也可以查看这段代码,并立即知道它的工作意义的数据形态

将所有内容放在一起

为了节省您收集上述所有代码段的时间,这里是一个带有验证输出的完整可运行脚本

function array_kmap (callable $f, iterable $xs) {
  return array_reduce(array_keys($xs), function ($acc, $k) use ($xs, $f) {
    return array_merge($acc, [$k => call_user_func($f, $k, $xs[$k], $xs)]);
  }, []);
}

function humanIdentifier ($x) {
  return ucwords(str_replace('-', ' ', $x));
}

function array_flatmap (callable $f, iterable $xs) {
  return array_reduce(array_map($f, $xs), 'array_merge', []);
}

function partial (callable $f, ...$xs) {
  return function (...$ys) use ($f, $xs) {
    return call_user_func($f, ...$xs, ...$ys);
  };
}

$a = [
  ['one' => 1, 'two-two' => 2, 'four' => 4],
  ['two-two' => 22, 'three' => 33, 'four' => 44]
];

$b = array_flatmap(partial('array_kmap', 'humanIdentifier'), $a);

print_r($b);
// Array
// (
//     [one] => One
//     [two-two] => Two Two
//     [four] => Four
//     [three] => Three
// )

<强>备注

与您在问题中发布的原始代码相比,我们在此处编写了大量代码。所以也许你想知道这是一个怎样的改进。嗯,这是一个定性测量,并且在很多方面(例如速度,效率),这个答案可能更糟。但在其他领域(例如可读性,可维护性),我看到了一个显着的进步。

和其他人一样,当我第一次阅读你的代码时,我正在摸不着它的所作所为。看看由此产生的转变,我可以更容易地推断出发生了什么,因为我不太关注 事情如何被转变,我可以专注于重要的部分。

如果你眯着眼睛,这基本上就是我们必须关注的一切

// input is array of arrays
$a = array(array( ... ))

// output requires map of map of input
$b = map(map( ... humanIdentifier ))

我们也做了很多其他事情,比如避免不必要的任务,重新分配或突变。创建$a后,$b不受影响。随着我们的计划不断发展,避免这样的副作用有助于提高可读性并降低复杂性。无论如何,这些都超出了这个答案的范围,但我想我会提到它们。

希望有所帮助^ _ ^

答案 2 :(得分:0)

不是一行,但您仍然可以删除换行符。

$output = [];
foreach ($arr as $subarray) { foreach ($subarray as $key => $value) {
    $output[$key] = ucwords(str_replace(array("_", "-"), " ", $key));
} }

答案 3 :(得分:0)

$arr = array(
    array("one" => 1, "two-two" => 2, "four" => 4),
    array("two-two" => 22, "three" => 33, "four" => 44)
);

以明确的功能方式:

$keys = array_keys(call_user_func_array('array_merge', $arr));
$formattedKeys = array_map(
    function($key) {return ucwords(str_replace(array('_', '-'), ' ', $key));},
    $keys
);
print_r(array_combine($keys, $formattedKeys));

或者只是简短地说:

$keys = array();
foreach(array_keys(call_user_func_array('array_merge', $arr)) as $key)
    $keys[$key] = ucwords(str_replace(array('_', '-'), ' ', $key));
print_r($keys);

或者:

$keys = array();
foreach(call_user_func_array('array_merge', $arr) as $key=>$unused)
    $keys[$key] = ucwords(str_replace(array('_', '-'), ' ', $key));
print_r($keys);

如果$arr始终包含两个项,您还可以将call_user_func_array('array_merge', $arr)替换为$arr[0]+$arr[1]array_merge($a, $b)$a+$b不同只在值中,而不是在键中。)