我有 2 个数组:
$haystack = array(
array(
'name' => 'Bill',
'city' => 'Rome',
'color' => 'blue'
),
array(
'name' => 'Jane',
'city' => 'Wien',
'color' => 'red'
)
);
$needles = array(
array(
'name' => 'Bill',
'city' => 'Rome',
'color' => 'red'
),
array(
'name' => 'Jane',
'city' => 'Wien',
'color' => 'red'
)
);
现在我想获取$needles
数组和$haystack
数组中存在(相同)的键值对的数量。结果应该是:
- 2 用于
$needles[0]
和$haystack[0]
- 0 表示
$needles[1]
和$haystack[0]
- 1 用于
$needles[0]
和$haystack[1]
- 3 用于
$needles[1]
和$haystack[1]
我知道,我可以运行丑陋的嵌套foreach
:
foreach($haystack as $hi => $arr) {
foreach($arr as $key => $val) {
foreach($needles as $ni => $needle) {
if (!isset($matches[$hi][$ni])) {
$matches[$hi][$ni] = 0;
}
foreach($needle as $k => $v) {
$key == $k && $val == $v && $matches[$hi][$ni]++;
}
}
}
}
print_r($matches);
但是有更好的方法吗?也许我错过了一些内置数组函数,或者我应该更多地研究迭代器?
更新:我想补充一点,我也有多针阵列。我用一维数组做了一个简单的例子,但也请考虑一下。
UPDATE2:我在示例中将$needles
更改为多维数组。很抱歉我从一开始就没有这样做,但我希望使用简单的针更容易理解,答案也可以让我知道如何为多维针修复它......
感谢您的帮助!
现在我想获取$needle数组和$haystack数组中存在(相同)的键值对的数量。
这应该符合要求:
return array_map(
function ($straw) use ($needle) {
return count(array_intersect_assoc($straw, $needle));
},
$haystack
);
它将通过保留相同的键来重现由"吸管"组成的大海捞针,但具有每个吸管和参考针之间的公共键对计数的值。
对于 $haystack[0],结果应为 2,对于 $haystack[1],结果应为 1。
事实上,对于您的输入数据,它会按预期返回:
Array
(
[0] => 2
[1] => 1
)
递归版本
在这种情况下,我们有一个带有键的大海捞针(和针),每个值都可以是一个数组。如果两个值相同,我们认为它们"相等",除了键顺序。可以通过比较两个数组的序列化来检查这种相同性;为了实现键顺序不变性,我们不序列化原始数组,而是序列化以确定性方式排序的键(和值键)的副本。
function deepmatches($haystack, $needle) {
// Function to "comb" an array so that all keys are in ascending order.
$keySort = function($arr, $ks) {
if (!is_array($arr)) {
return $arr;
}
ksort($arr, SORT_STRING);
return array_map(
function ($values) use ($ks) {
return $ks($values, $ks);
},
$arr
);
};
return array_map(
function ($straw) use ($needle, $keySort) {
return count(
array_uintersect_assoc($straw, $needle,
function($val1, $val2) use ($keySort) {
if (is_array($val1)) {
if (!is_array($val2)) { return -1; }
// Both arrays.
$val1 = serialize($keySort($val1, $keySort));
$val2 = serialize($keySort($val2, $keySort));
} else {
if (is_array($val2)) { return 1; }
}
if ($val1 < $val2) { return -1; }
if ($val1 > $val2) { return 1; }
return 0;
}
)
);
},
$haystack
);
}
当值为字符串时,行为是相同的。否则,将以递归方式考虑值。
$needle = array(
'name' => 'Bill',
'city' => array(
'country' => 'Italy',
'name' => 'Rome'
),
'color' => 'red'
);
实际案例
这里我们没有递归数组,而是数组数组;我们用针映射大海捞针:
return array_map(
function ($straw) use ($needles) {
// $straw is one Element, $needles is an array of.
return array_map(
function ($needle) use ($straw) {
// How many elements are common between $needle and $straw
return count(array_intersect_assoc($straw, $needle));
},
$needles
);
},
$haystack
);
这将返回具有相同基数 $haystack 的数组,其中包含具有相同基数 $needles 的数组。所以
$ret[5][2]
包含干草堆的第六个元素和第三根针之间有多少个公共元素。
鉴于主要的时间支出是intersect_assoc,foreach() 构造很可能会更快。闭包需要调整范围并执行堆栈,而 foreach 不需要:
$ret = [ ];
foreach ($haystack as $kh => $straw) {
$row = [ ];
foreach ($needles as $kn => $needle) {
$row[$kn] = count(array_intersect_assoc($straw, $needle));
}
$ret[$kh] = $row;
}
unset($row);
计算array_intersect_assoc的结果应该会稍微简化代码。
foreach ($haystack as $i => $arr) {
$matches = count(array_intersect_assoc($needle, $arr));
echo "Needle found {$matches} times in haystack at index {$i} 'n";
}