合并和比较二维数组以找出共同的值。

8

我正在尝试循环遍历多个数据库表结构,并确定它们的共同结构(即哪些列是相同的)。最终的结构应该只显示共同的列,因此如果任何一个表具有唯一的列,则不应在最终数组中出现。

以下是三个表结构可能看起来像的示例:

$arr1 = [
    ["name"=>"col1", "type"=>"varchar"],
    ["name"=>"col2", "type"=>"int"]    
];
$arr2 = [
    ["name"=>"col1", "type"=>"varchar"],
    ["name"=>"col2", "type"=>"int"]    ,
    ["name"=>"col3", "type"=>"date"]    
];
$arr3 = [
    ["name"=>"col1", "type"=>"varchar"],
    ["name"=>"col3", "type"=>"int"]    
];

$arrays = [$arr1, $arr2, $arr3];

使用array_mergearray_diffarray_intersect或循环,是否可以确定这些列哪些在所有表中都存在?
最终值应该是[["name"=>"col1", "type"=>"varchar"]]

你迄今为止的努力是什么?能否包含代码。 - Nigel Ren
@NigelRen 本质上就是许多皱巴巴的纸片。循环中嵌套循环,每列都有计数器,修剪、合并、交集、差分...尝试了很多次,但我已经放弃了。 - billy_bones_3
你尝试过使用array_intersect_assoc()函数吗? - jbe
5个回答

5
你可以使用 array_uintersect 函数,该函数可以使用自定义函数进行交集操作。但是需要注意,比较函数并不仅仅返回 true 或 false,而是需要返回 -101
在 PHP7 中,你可以使用太空船操作符 <=> 进行比较。
$intersection = array_uintersect($arr1, $arr2, $arr3, function($a, $b) {
    $ret = $a['name'] <=> $b['name'];
    return $ret ? $ret : $a['type'] <=> $b['type'];
});
print_r($intersection);

如果你想把所有的数组放在交集里,你可以这样做:

$arrays = [$arr1, $arr2, $arr3];
$arrays[] = function($a, $b) {
    $ret = $a['name'] <=> $b['name'];
    return $ret ? $ret : $a['type'] <=> $b['type'];
};

$intersection = array_uintersect(...$arrays);

在旧版本中,你应该使用 strcasecmp

非常好,我也喜欢你帮助他使用可变参数。 - Timmetje

3
您可以使用自定义比较方法与array_uintersect()一起使用:
$arr1 = [
    ["name" => "col1", "type" => "varchar"],
    ["name" => "col2", "type" => "int"]
];
$arr2 = [
    ["name" => "col1", "type" => "varchar"],
    ["name" => "col2", "type" => "int"],
    ["name" => "col3", "type" => "date"]
];
$arr3 = [
    ["name" => "col1", "type" => "varchar"],
    ["name" => "col3", "type" => "int"]
];

$common_columns = array_uintersect($arr1, $arr2, $arr3, 'compareDeepValue');

print_r($common_columns);

function compareDeepValue($val1, $val2)
{
    return (strcasecmp(serialize($val1), serialize($val2))) ;
}

输出结果为:

Array
(
    [0] => Array
        (
            [name] => col1
            [type] => varchar
        )

)

注意:

@Abracadaver提出了一个很好的观点,这种方法只有在数组约定的顺序相同的情况下才能正常工作。

那么你可以例如使用这个:

function compareDeepValue($val1, $val2)
{
    return ($val1['name'] === $val2['name'] && $val1['type'] === $val2['type']) ? 0 : -1;
}

2
只有当它们始终按相同顺序排列时才有效,尝试 ["type"=>"varchar", "name"=>"col1"] 或更多值 ["name"=>"col1", "type"=>"varchar", "default"=>"NULL"] - AbraCadaver
好的观点,但目标是让OP可以使用他自己的自定义方法,当然他可以根据自己的需要进行更改。我会添加注释,谢谢。 - Timmetje
到目前为止,这个程序是可以工作的。现在我只是不明白为什么我不能在参数中使用扩展运算符,例如 array_uintersect($arr1, ...$arrays),假设我从 $arrays 中取出了 $arr1 - billy_bones_3
1
array_u*函数不是可变参数的,因为它们都在可变参数列表之后带有最终的回调参数(或两个参数),这与可变参数的工作规则不符。 - Timmetje

1
你可以通过 name 键提取数组和索引,并使用键计算交集:
$result = array_intersect_key(array_column($arr1, null, 'name'),
                              array_column($arr2, null, 'name'),
                              array_column($arr3, null, 'name'));

产出:

Array
(
    [col1] => Array
        (
            [name] => col1
            [type] => varchar
        )
)

如果需要,使用array_values返回数字索引。

值得一提的是,这根本不比较“类型”的值。 - billyonecan
@billyonecan 正确——确定所有表中哪些“列”是共同的。 - AbraCadaver

0

可能吗?

$arrays = [$arr1, $arr2, $arr3];
$arrays_extended = [];

foreach($arrays as $row => $innerArray){
  foreach($innerArray as $innerRow => $value){
    array_push($arrays_extended, $value);
  }
}

var_dump(array_unique($arrays_extended));

输出 [["name"=>"col1", "type"=>"varchar"]]

3
我没有给你的帖子投反对票,但是这段代码因为array_unique函数的错误使用导致了很多通知。而且,总体上看这段代码有些出毛病。链接:https://3v4l.org/1okcm - Xatenev
我没有在在线PHP编辑器上尝试过它,对我来说它没有抛出任何异常。但是感谢您的解释。 - Valentin P
1
这可能是因为您的 error_reporting / display_errors 配置不正确。请参阅 如何配置开发环境下的错误报告 - Xatenev

0

方法:

1.按以下方式将元素转换为字符串:

 array(2) {
      [0] =>
      string(32) "{"name":"col1","type":"varchar"}"
      [1] =>
      string(28) "{"name":"col2","type":"int"}"
    }
    array(3) {
      [0] =>
      string(32) "{"name":"col1","type":"varchar"}"
      [1] =>
      string(28) "{"name":"col2","type":"int"}"
      [2] =>
      string(29) "{"name":"col3","type":"date"}"
    }
    array(2) {
      [0] =>
      string(32) "{"name":"col1","type":"varchar"}"
      [1] =>
      string(28) "{"name":"col3","type":"int"}"
    }

2.使用数组交集函数查找共同元素。

3.将结果转换回数组。

$arr1 = [
    ["name"=>"col1", "type"=>"varchar"],
    ["name"=>"col2", "type"=>"int"]    
];
$arr2 = [
    ["name"=>"col1", "type"=>"varchar"],
    ["name"=>"col2", "type"=>"int"]    ,
    ["name"=>"col3", "type"=>"date"]    
];
$arr3 = [
    ["name"=>"col1", "type"=>"varchar"],
    ["name"=>"col3", "type"=>"int"]    
];

list($darr1, $darr2, $darr3) = convertArrToStr($arr1, $arr2, $arr3);
/* output:
array(2) {
  [0] =>
  string(32) "{"name":"col1","type":"varchar"}"
  [1] =>
  string(28) "{"name":"col2","type":"int"}"
}
array(3) {
  [0] =>
  string(32) "{"name":"col1","type":"varchar"}"
  [1] =>
  string(28) "{"name":"col2","type":"int"}"
  [2] =>
  string(29) "{"name":"col3","type":"date"}"
}
array(2) {
  [0] =>
  string(32) "{"name":"col1","type":"varchar"}"
  [1] =>
  string(28) "{"name":"col3","type":"int"}"
}
 */
var_dump(duplicates($darr1, $darr2, $darr3));
/* output:
array(1) {
  [0] =>
  array(2) {
    'name' =>
    string(4) "col1"
    'type' =>
    string(7) "varchar"
  }
}
*/    

function convertArrToStr() {
    $args = func_get_args();
    foreach($args as &$arg){
          foreach($arg as $k => $arr) {
                      $arg[$k] = json_encode($arr, true);
          }
    }
    return $args;
}

function duplicates($darr1, $darr2, $darr3) {
    $intersects = array_intersect($darr1, $darr2, $darr3);
    $r = [];
    foreach($intersects as $v) {
          $r[] = json_decode($v, true);    
    }
    return $r;
}

希望这可以帮助您编写更优雅的解决方案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接