PHP:array_uintersect()意外行为

时间:2016-10-27 15:43:13

标签: php arrays set-intersection array-intersect

测试脚本

$i = 0;
array_uintersect(['foo', 'bar'], ['baz', 'qux'], function($a, $b) use (&$i) {
    print_r([$a, $b, $i++]);
});

实际结果

Array
(
    [0] => bar
    [1] => foo
    [2] => 0
)
Array
(
    [0] => qux
    [1] => baz
    [2] => 1
)
Array
(
    [0] => bar
    [1] => qux
    [2] => 2
)
Array
(
    [0] => bar
    [1] => foo
    [2] => 3
)

预期结果

Array
(
    [0] => foo
    [1] => baz
    [2] => 0
)
Array
(
    [0] => bar
    [1] => qux
    [2] => 1
)

换句话说,我期望传递给回调的是左数组的当前元素和右数组的当前元素。

此外,如果我将另一个数组传递给array_uintersect,我希望应用相同的逻辑 - 例如,将另一个参数传递给回调($c)。

有人可以解释这种行为吗?

5 个答案:

答案 0 :(得分:2)

我不知道除了比较数组的值之外,你为什么期望比较回调中的任何东西。回调的唯一目的是比较两个数组中的下一对项目。

该函数返回两个数组的交集结果。在回调中,您表达了如何比较值的想法。例如,以下代码假定应通过比较字符串的第一个字符来执行交集:

$a = array_uintersect(['foo', 'bar'], ['baz', 'qux'], function($a, $b) {
  return strcmp($a[0], $b[0]);
});

print_r($a);

输出

Array
(
    [1] => bar
)

传递给回调的项的顺序由PHP内部指定,并且将来可能很容易改变。

因此除了比较两个变量之外,比较函数不应该做任何事情。在官方文档中,任何其他目的都没有使用回调的暗示。

答案 1 :(得分:1)

我相信前两个调用用于在内部算法中为变量设定种子。但是既然你没有返回算法可以用来确定相等/排序的任何东西,那么它只运行接下来的两个。

如果您实际返回01-1,那么您会看到计算交叉点所需的完整比较链:

$i = 0;
array_uintersect(['foo', 'bar'], ['baz', 'qux'], function($a, $b) use (&$i) {
    print_r([$a, $b, $i++]);

    if ($a === $b) return 0;
    if ($a  >  $b) return 1;
    return -1;
});

收率:

Array
(
    [0] => bar
    [1] => foo
    [2] => 0
)
Array
(
    [0] => qux
    [1] => baz
    [2] => 1
)
Array
(
    [0] => bar
    [1] => baz
    [2] => 2
)
Array
(
    [0] => foo
    [1] => baz
    [2] => 3
)
Array
(
    [0] => foo
    [1] => baz
    [2] => 4
)
Array
(
    [0] => foo
    [1] => qux
    [2] => 5
)

答案 2 :(得分:1)

array_uintersect docs中未提及的内容是PHP sorts all the arrays首先从左到右。只有数组排序后,PHP才会将它们(再次,从左到右)移动以找到交集。

第三个参数(比较函数)传递给内部排序算法,而不是交叉算法。因此,看到的调试输出是计算排序的排序算法。

zend_sort implementation通常使用uses a bisecting quick sort implementation。对于示例中大小的数组,PHP使用插入排序。对于大型数组,PHP使用3或5点的轴,以便improve worst-case complexity

由于您没有显式地从比较函数返回任何值,因此PHP默认返回null(0),并且由于PHP使用插入排序,因此您会看到O(n * n)行为,因为排序所有组合。

答案 3 :(得分:0)

我认为你正在寻找这个;)

$result = array_map(function($a, $b) {
    return [$a, $b];
}, ['foo', 'bar'], ['baz', 'qux']);
var_dump($result);

这将输出

array(2) {
  [0]=>
  array(2) {
    [0]=>
    string(3) "foo"
    [1]=>
    string(3) "baz"
  }
  [1]=>
  array(2) {
    [0]=>
    string(3) "bar"
    [1]=>
    string(3) "qux"
  }
}

更新:它使用array_uintersect方法返回您想要的结果。它不是最有效的方法,并且没有使用不同的数据集等进行测试但是应该可以工作。

$entities = [
    [
        'id' => 1,
        'timestamp' => 1234
    ],
    [
        'id' => 2,
        'timestamp' => 12345
    ],
    [
        'id' => 3,
        'timestamp' => 123456
    ],
    [
        'id' => 8,
        'timestamp' => 123456
    ],
    [
        'id' => 10,
        'timestamp' => 123456
    ],
    [
        'id' => 11,
        'timestamp' => 123456
    ],
    [
        'id' => 12,
        'timestamp' => 123456
    ]
];

$identities = [1, 11, 2, 8, 10];

$result = array_uintersect($entities, $identities, function($a, $b) {

    // Both array skip
    if (is_array($a) && is_array($b)) {
        if ($a['id'] > $b['id']) {
            return 1;
        }
        return -1;
    }

    // Both int skip
    if (is_int($a) && is_int($b)) {
        if ($a > $b) {
            return 1;
        }
        return -1;
    }

    // $a is array
    if (is_array($a)) {
        if ($a['id'] == $b) {
            return 0;
        }
        elseif ($a['id'] > $b) {
            return 1;
        }
        return -1;
    }

    // $b is array
    if($b['id'] == $a) {
        return 0;
    }
    if($a > $b['id']) {
        return 1;
    }

    return -1;
});
var_dump($result);

和结果

array(5) {
  [0]=>
  array(2) {
    ["id"]=>
    int(1)
    ["timestamp"]=>
    int(1234)
  }
  [1]=>
  array(2) {
    ["id"]=>
    int(2)
    ["timestamp"]=>
    int(12345)
  }
  [3]=>
  array(2) {
    ["id"]=>
    int(8)
    ["timestamp"]=>
    int(123456)
  }
  [4]=>
  array(2) {
    ["id"]=>
    int(10)
    ["timestamp"]=>
    int(123456)
  }
  [5]=>
  array(2) {
    ["id"]=>
    int(11)
    ["timestamp"]=>
    int(123456)
  }
}

答案 4 :(得分:-4)

<?php
    $i  = 0;
    $r1 = ['foo', 'bar'];
    $r2 = ['baz', 'qux'];
    $result = array_uintersect($r1, $r2, function($a, $b){
        return ($a[0]> $b[0]);
    });


    var_dump($result);
    // YIELDS::
    array (size=2)
      0 => string 'foo' (length=3)
      1 => string 'bar' (length=3)