问题 保持数组的偶数元素?


说我有一个 $input 数组,包含这样的内容:

array
  0 => string 'a' (length=1)
  1 => string 'b' (length=1)
  2 => string 'c' (length=1)
  3 => string 'd' (length=1)
  4 => string 'e' (length=1)
  5 => string 'f' (length=1)
  6 => string 'g' (length=1)
  7 => string 'h' (length=1)
  8 => string 'i' (length=1)
  9 => string 'j' (length=1)

我想得到一个 $output 数组,包含这个:

array
  0 => string 'a' (length=1)
  1 => string 'c' (length=1)
  2 => string 'e' (length=1)
  3 => string 'g' (length=1)
  4 => string 'i' (length=1)

$output 数组包含一半的值 $input ;那些在输入中有偶数键的键;第一个是保留,第二个不是,第三个是,所以一个......

(注意:不保留键;只有值很重要)

我怎么能这样做?只保留一个数组的两个值?


我已经尝试了一些想法,并且已经有了几个不同的解决方案:

第一个想法: 迭代输入数组,并将有趣的值复制到输出数组:

$input = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', );
$output = array();

$nbr = count($input);
for ($i = 0 ; $i < $nbr ; $i += 2) {
    $output[] = $input[$i];
}

var_dump(array_values($output));

第二个想法: 迭代数组,然后 unset 我不想保留的内容:

$input = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', );
$output = $input;

$nbr = count($input);
for ($i = 1 ; $i < $nbr ; $i += 2) {
    unset($output[$i]);
}

var_dump(array_values($output));

第三个想法: 使用组合 array_fliprangearray_diff_key,...:

$input = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', );
$output = array();

$keys_to_exclude = array_flip(range(1, count($input)-1, 2));
$output = array_diff_key($input, $keys_to_exclude);

var_dump(array_values($output));

第四个想法: 关于同样的事情,但有 array_intersect_key

$input = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', );
$output = array();

$keys_to_include = array_flip(range(0, count($input)-1, 2));
$output = array_intersect_key($input, $keys_to_include);

var_dump(array_values($output));

还有其他想法吗?甚至/特别是如果它听起来有点hacky或任何东西?

我的目标不是获得最有效和最简单的语法;它只是为了好玩,因为我很好奇,实际上是^^

如果标题没有使用正确的词语来描述我想要的东西,请不要犹豫;或编辑它  :-)


2253
2017-08-02 10:13


起源

我根据要求稍微编辑了算法的标题和描述。就个人而言,我会使用第一个想法 - 它很简单,也很清楚你在做什么。 - John Carter
感谢您的编辑:-)嗯,实际上,“我怎么能在没有自己循环的情况下做到这一点”是一个几天前在工作中加速的问题;一位同事,我想到了......我们终于使用了“for”解决方案(我给的第一个):不那么有趣,但是当有人必须维护我们的代码时更容易理解 - 这是最好的一个在我们的工作中很重要;但是,这仍然是一个有趣的问题,我想我可能会在这里得到一些有趣/有趣的命题^^ - Pascal MARTIN
对于有自己想法的精心设计的问题+1 - PatrikAkerstrand


答案:


<?php
$x = range('a', 'f');

$x = array_map('array_shift', 
       array_chunk($x, 2)
     );

var_dump($x);

或另一个

<?php
class ArrayEvenIterator extends ArrayIterator {
    public function next() {
        parent::next();
        return parent::next();
    }
}

$x = range('a', 'f');
$x = iterator_to_array(new ArrayEvenIterator( $x ), false);

var_dump($x);

或者使用php 5.3闭包(在这种情况下不比全局更好;-))

<?php
$x = range('a', 'f');

$x = array_filter( $x, function($e) use(&$c) { return 0===$c++%2; });

var_dump($x);

15
2017-08-02 10:34



还不错!谢谢:-)没有考虑使用一个闭包(我的思想还没有用于PHP中的那些,我猜^^(我不能像我想的那样使用PHP 5.3 :-()) - Pascal MARTIN
+1使用闭包和 iterator_to_array (我不知道)。 - Luiz Damim


假设数字键:

foreach ($array as $key => $value) {
    if ($key % 2 != 0) {
        unset($array[$key]);
    }
}

编辑

这是一个稍微更疯狂的解决方案,它使索引保持连续而不重新索引。 ; O)

foreach ($array as $key => $value) {
    if (!($key%2)) {
        $array[$key/2] = $value;
    }
}
$array = array_slice($array, 0, ceil(count($array)/2));

2
2017-08-02 10:20



@Pascal(OP):我不得不承认这与你的不太相似 for 解决方案,但我认为你已经覆盖了几乎所有的基础。大多数理智的解决方案应该只在语法糖方面有所不同。 :) - deceze♦
这也是我的建议。如果键不是数字,则可以通过函数运行数组,并使用数字键创建新的元素数组。然后,每个元素将是原始数组中的键/值对的数组。关于Modulus运算符的好处是它可以用来查找各种元素。要查找每个第5个元素if($ key%5 == 0)这意味着除以5并返回余数。列表0到15的元素将返回0,5,10和15,每个元素在除以5时返回0的余数。例如10/5 = 2 R0。 - Gordon Potter
@deceze:是的,你是对的,没有“奇迹”和可维持的解决方案^^但那很有趣的是;-)无论如何,谢谢,没想到模数! - Pascal MARTIN
请注意,此版本保留了输入键([0] => a [2] => c [4] => e [6] => g [8] => i),这可能很有用,但不会t匹配指定的输出。在输出上使用array_values($ array)当然会实现这一点。 - John Carter
我真的不在乎这种情况下的钥匙;但是,如果有一天我需要那些,那就太好了。谢谢! - Pascal MARTIN


如果您使用的是PHP 5.3或更高版本,或者安装了SPL扩展(默认情况下您将在PHP 5上使用),则可以使用 FilterIterator 和 ArrayObject的 类。

class EvenKeysFilter extends FilterIterator
{
    private function iseven($keyval)
    {
        return (($keyval % 2) == 0);
    }

    public function accept()
    {
        $keyval = $this->getInnerIterator()->key();
        return ($this->iseven($keyval));
    }
}

$input = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', );
$inputobj = new ArrayObject($input);   

$evenFilterIter = new EvenKeysFilter($inputobj->getIterator());    
$output = iterator_to_array($evenFilterIter, false);

print_r($output);

(道具到 VolkerK 指出 iterator_to_array()

哪个正确输出:

Array
(
    [0] => a
    [1] => c
    [2] => e
    [3] => g
    [4] => i
)

2
2017-08-02 11:03



不知道为什么,但我想我喜欢这个!不可能在其他人必须维护的应用程序中使用它,但我绝对喜欢它(并且没有考虑SPL)。谢谢:-)(只是'('必须在接受方法中删除,然后它的工作原理) - Pascal MARTIN
我同意,对于这种情况,SPL可能有些过分,但是像RecursiveIteratorIterator这样的东西是非常有价值的。 - John Carter
人们(包括我自己,实际上)通常不足够使用SPL太糟糕了:-( - Pascal MARTIN
+1用于SPL的聪明使用。我需要多关注它:) - Luiz Damim


不一定是最有效的方法,但既然你提到这不一定是一个要求......

翻转,过滤,然后翻转。

<?php
    function even($var)
    {
        return(!($var & 1));
    }

    $input = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', );
    $flipped = array_flip($input);
    $filtered = array_filter($flipped, 'even');
    $output = array_flip($filtered);
?>

0
2017-08-02 10:21



根据输入,这可能会产生意外/不直观的结果。例如。 $ input = array('a','a','b'); - > $ output == array('b') - VolkerK


创建一个包装函数

function getInput($i)
{
     global $input;
     return $input[$i*2];
}

我认为最小,最有效。


-1
2017-08-02 10:28



它当然很小,但是它做了什么? - deceze♦
哎呀,全球! :P - John Carter


  function dropHalf($a){
     $f=0;
     foreach($a as $k=>$v)
       if($f = ! $f)
         unset($a[$k]);
     return $a;
  }

这是我能想到的最小版本。


-2
2017-08-02 10:24



你错过了什么吗? - deceze♦
你的if语句不会工作伙伴。 - Ayaz Alavi