加权数组基于键相交,平均值为

时间:2017-11-08 17:42:37

标签: php arrays average array-intersect

请考虑以下这个集合:

$collection = Array ( 
    $arr1 = Array ( 1 => 10.0, 2 => 20.0, 3 => 50.0, 4 => 80.0, 5 => 100.0 ),
    $arr2 = Array ( 3 => 20.0, 5 => 20.0, 6 => 100.0, 7 => 10.0 ),
    $arr3 = Array ( 1 => 30.0, 3 => 30.0, 5 => 10.0, 8 => 10.0 )
);

考虑这个基于$ collection中包含的数组的交集的理论输出,考虑它们的数组键与基于单个值的平均值的相应值:

$output = Array ( 3 => 33.3333, 5 => 43.3333 );

可以用优雅的方式使用像array_intersect_ *这样的本机PHP函数来解决这个问题吗?

如果没有,你能否建议我一个优雅的解决方案,并不一定需要一个外在丑陋的foreach?

请记住,需要相交的数组数量不固定。它可以是2个输入数组,因为它可以是1000个输入数组。 键将始终为整数,值将始终为浮点数或整数。

换句话说:

$collection = [
    $arr1 = [ ... ];
    $arr2 = [ ... ];
    $arr3 = [ ... ];
    ...
    $arrn = [ ... ];
];
$output = [ intersected and weighted array based (on comparison) on keys from $arr1 to $arrn, and (on values) from the value averages ];

4 个答案:

答案 0 :(得分:2)

计算一次输入数组。

$n = count($collection);

按键计算所有子阵列的交集。

$intersection = array_intersect_key(...$collection);
// PHP5: $intersection = call_user_func_array('array_intersect_key', $input);

通过对交叉点中每个键的输入数组中的列进行平均来构建结果。

$output = [];
foreach ($intersection as $key => $value) {
    $output[$key] = array_sum(array_column($collection, $key)) / $n;
}

如果您真的想要完全避免使用foreach,则可以使用array_map

$output = array_map(function($key) use ($collection, $n) {
    return array_sum(array_column($collection, $key)) / $n;
}, array_keys($intersection));

但在我看来,这只会增加不必要的复杂性。

注意:$intersection中的值将是第一个子数组中的单个值,但它们并不重要;在生成输出时,它们被忽略了。如果你在foreach中有一个无用的$value变量,那么你可以改为foreach (array_keys($intersection) as $key),但我选择避免不必要的函数调用。

答案 1 :(得分:1)

您可以将数组合并为一个,并使用array_sum和count()来获得平均值。

$arr1 = Array ( 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100 );
$arr2 = Array ( 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10 );
$arr3 = Array ( 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10 );
$array = array_merge_recursive($arr1,$arr2,$arr3);

$key= "two";
If(is_array($array[$key])){
    $avg = array_sum($array[$key])/count($array[$key]);
}Else{
    $avg = $array[$key];
}

Echo $avg;

https://3v4l.org/pa3PH

<小时/> 编辑以关注$ collection数组。

然后尝试一下。使用数组列获取正确的键并使用array_sum并计数以获得平均值。

$collection = array(
    Array ( 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100 ),
    Array ( 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10 ),
    Array ( 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10 ));

$key= "three";
$array = array_column($collection, $key);

If(count($array) != 1){
    $avg = array_sum($array)/count($array);
}Else{
    $avg = $array[0];
}

Echo $avg;

https://3v4l.org/QPsiS


最终编辑。

这里我遍历第一个子阵列并使用数组列找到所有匹配的键 如果密钥数与收集计数相同,则密钥存在于所有子阵列中,应该“保存”。

$collection = array(
    Array ( 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100 ),
    Array ( 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10 ),
    Array ( 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10 ));

Foreach($collection[0] as $key => $val){
    $array = array_column($collection, $key);
    If(count($array) == count($collection)){
        $avg[$key] = array_sum($array)/count($array);
    }
}
Var_dump($avg);

https://3v4l.org/LfktH

答案 2 :(得分:1)

我想可以这样做:

<?php

$intersecting_arrays = Array (
    0 => Array ( 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100 ),
    1 => Array ( 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10 ),
    2 => Array ( 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10 )
    );

$temp = $intersecting_arrays[0];
for($i = 1; $i < count($intersecting_arrays); $i++) {
    $temp = array_intersect_key($temp, $intersecting_arrays[$i]);
}

$result = Array();
foreach(array_keys($temp) as $key => $val) {
    $value = 0;
    foreach($intersecting_arrays as $val1) {
        $value+= $val1[$val];
    }
    $result[$key] = $value / count($intersecting_arrays);
}

print_r($temp);
print_r($result);

https://3v4l.org/j8o75

以这种方式,它并不取决于您拥有多少阵列。 在这里,您可以获得所有阵列中的交叉点,然后使用收集的键计算平均值。

答案 3 :(得分:1)

好的,对于未知数量的输入数组,我肯定会先使用两个嵌套的foreach循环来将它们组合起来 - 将未知数字放入array_merge_recursive或类似的将是困难的。

$input = [
  0 => [ 'one' => 10, 'two' => 20, 'three' => 50, 'four' => 80, 'five' => 100],
  1 => [ 'three' => 20, 'five' => 20, 'six' => 100, 'seven' => 10],
  2 => [ 'one' => 30, 'three' => 30, 'five' => 10, 'eight' => 10]
];

$combined = [];
foreach($input as $array) {
  foreach($array as $key => $value) {
    $combined[$key][] = $value;
  }
}

$averages = array_map(function($item) {
  return array_sum($item)/count($item);
}, $combined);

var_dump($averages);

https://3v4l.org/hmtj5

请注意,此解决方案不需要在array_map回调中检查数组与单个整数,因为与array_merge_recursive不同,循环内的$combined[$key][]会看到即使只有一个值的键也会具有该值在数组中。

编辑:

  

但请记住,并非所有的密钥都会被考虑在内

啊,好吧,所以你只想要那些多次出现的键的平均值。通过在使用array_map之前过滤组合数组,可以很容易地解决这个问题:

$combined = array_filter($combined, function($v, $k) {
  return count($v) != 1;
}, ARRAY_FILTER_USE_BOTH );

集成到上述解决方案中:https://3v4l.org/dn5ro

编辑#2

  

[Andreas的评论]我认为“one”不应该在输出中,因为它不在所有三个数组中。

啊,我看到......即使从例子中也看不出这是实际需要的结果:-)然后我的过滤必须再次修改一下,并考虑输入数组的数量: / p>

$combined = array_filter($combined, function($v, $k) use($input) {
  return count($v) == count($input);
}, ARRAY_FILTER_USE_BOTH );

https://3v4l.org/9H086