3

假设我有一组标签

$all_tags = array('A', 'B', 'C');

我想创建一组带有 $_GET 变量的 URL。
我希望链接是:
'A'链接到"index.php?x[]=B&x[]=C"
'B'链接到"index.php?x[]=A&x[]=C"
等($_GET 是一个包含除“当前”元素之外的所有元素的数组)(我知道有一种更简单的方法来实现这个:我实际上是在简化一个更复杂的情况)

我想用它array_filter()来解决这个问题。
这是我的尝试:

function make_get ($tag) { return 'x[]=' . $tag; }
function tag_to_url ($tag_name) {
   global $all_tags;

   $filta = create_function('$x', 'global $all_tags; return ($x != $tag_name);'); 
   return 'index.php?' . implode('&', array_map("make_get", array_filter($all_tags, "filta")));
}
print_r(array_map("", $all_tags));

但它不起作用。我怀疑这可能与 PHP 中的映射和过滤器如何实际改变数据结构本身并返回一个布尔值有关,而不是使用函数式样式,它们不会改变并返回一个新列表。

我也对其他使代码更简洁的方法感兴趣。

4

4 回答 4

2

这是另一种方法:

// The meat of the matter
function get_link($array, $tag) {
    $parts = array_reduce($array, function($result, $item) use($tag)
                          {
                              if($item != $tag) $result[] = 'x[]='.$tag;
                              return $result;
                          });
    return implode('&', $parts);
}

// Test driver

$all_tags = array('A', 'B', 'C');

echo get_link($all_tags, 'A');
echo "\n";
echo get_link($all_tags, 'B');
echo "\n";
echo get_link($all_tags, 'C');
echo "\n";

这只是一次调用array_reduce,然后implode将结果一起拉到查询字符串中。

于 2011-12-07T18:56:36.090 回答
1

根据我在评论中给出的答案(此处显示):

<?php

  $all_tags = array('A', 'B', 'C');

  function tag_to_url($tag_name)
  {
    global $all_tags;

    $remaining_tags = array_diff($all_tags, array($tag_name));
    return sprintf('index.php?%s', 
             http_build_query(array('x'=>array_values($remaining_tags))));
  }

  echo tag_to_url('B'); // index.php?x%5B0%5D=A&x%5B1%5D=C
                        // basically: index.php?x[0]=A&x[1]=C

基本上,用于array_diff从数组中删除条目(而不是过滤),然后将其传递给http_build_query以提供有效的 URL。

于 2011-12-07T18:49:02.503 回答
1

我只是要回答“为什么它不起作用”部分。

$filta = create_function('$x', 'global $all_tags; return ($x != $tag_name);'); 

tag_name 变量在您的 lambda 函数的范围内未定义。现在,它在创建函数范围(tag_to_url)中定义。您也不能在这里完全使用 global 关键字,因为 $tag_name 不在全局范围内,它在本地 tag_to_url 范围内。您可以在全局范围内声明变量,然后它就可以工作,但考虑到您喜欢函数式方法,我怀疑您是否喜欢全局变量 :)

如果需要,您可以使用字符串连接和 var_export($tag_name) 进行欺骗并将其传递给 create_function(),但还有其他更好的方法可以实现您的目标。

另外,顺便说一句,我猜你可能会受益于在开发时调高 php 的错误报告级别。php 会向您抛出未定义的变量通知,这有助于调试和理解。

// ideally set these in php.ini instead of in the script
error_reporting(E_ALL);
ini_set('display_errors', 1);
于 2011-12-07T19:01:57.697 回答
1

在 PHP 中接近真正支持函数式编程风格的东西是非常非常新的——只有在 PHP 5.3 中,函数才成为一流的并且匿名函数成为可能。

顺便说一句,你不应该使用create_function(). 它真正做的是在全局命名空间中定义一个新函数(它永远不会被垃圾收集!),它eval()在幕后使用。

如果你有 PHP 5.3 或更高版本,你可以这样做:

$all_tags = array('A', 'B', 'C');

function is_not_equal($a, $b) {
    return $a != $b;
}

function array_filter_tagname($alltags, $name) {
    $isNotEqualName = function($item) use ($name){
        return is_not_equal($item, $name);
    };
    // array_merge() is ONLY to rekey integer keys sequentially.
    // array_filter() preserves keys.
    return array_merge(array_filter($alltags, $isNotEqualName));
}

function make_url($arr) {
    return 'input.php?'.http_build_query(array('x'=>$arr));
}
$res = array_filter_tagname($all_tags, 'B');
print_r($res);
print_r(make_url($res));

如果你有一个 PHP < 5.3,你应该为你的闭包使用一个类+对象而不是create_function().

class NotEqualName {
    protected $otheritem;
    function __construct($otheritem) { // with PHP 4, use "function NotEqualName($otheritem) {"
        $this->otheritem = $otheritem;
    }
    function compare($item) {
        return $item != $this->otheritem;
    }
}

function array_filter_tagname_objectcallback($alltags, $name) {
    $isNotEqualName = new NotEqualName($name);
    return array_merge(array_filter($alltags, array($isNotEqualName,'compare')));
}

然而,一般来说,PHP 不太适合函数式风格,并且对于您的特定任务,使用array_filter()的 PHP 不是非常惯用的。array_diff()是一种更好的方法。

于 2011-12-07T19:17:50.780 回答