比较数组并查找匹配值

4
我有两个数组$arr1$arr2$arr1是我期望从Excel文件中读取的列的列表,$arr2是实际找到的列的数组。
有时上传的文件包含:
  • 拼写错误的列名
  • 列的顺序不同
  • 可能缺少一些列
  • 此外,列名可能包含不同字符集(例如看起来像拉丁字母M的希腊字母'M',但不能视为相同)。
例如,假设我们有以下两个数组:
$arr1 = array('Action', 'LotSize', 'QuantityMinimum', 'SupplierName', 'SPN',
 'PartNumExt', 'UOM', 'ListPrice', 'MPN', 'MFrName', 'CatLevel1', 'CatLevel2',
 'CatLevel3', 'CatLevel4', 'CatLevel5', 'CatLevel6', 'AcctLevel1', 'AcctLevel2', 
 'AcctLevel3', 'AcctLevel4', 'AcctLevel5', 'AcctLevel6', 'Desc1', 'Desc2', 'PicName',
 'SupplierURL', 'CatPart','TechSpec', 'Kad');    

$arr2 = array('Action', 'LotSze', 'QuantityMinimum', 'SupplierName', 'SPN',
 'PartNumEx', 'UOM', 'ListPric', 'MPN', 'MfrName', 'CatLevel1', 'CatLevel2',
 'CatLevel3', 'CatLevel4', 'AcctLevel1', 'AcctLevel2', 'AcctLevel3', 'AcctLevel4',
 'Desc1', 'Desc2', 'PicName', 'SupplierURL', 'CatPart');

我需要比较这两个数组,并将匹配元素的位置保存到第三个数组中:
$arr3 = ([0]=>0, [1]=>1, [2]=>3, [3]=>5, [4]=>6, [5]=>...);

显示$arr1中每个匹配元素在$arr2中的位置。

所谓“匹配”,指的是所有相同的元素(例如Action),或部分相同的元素(例如TestTes),以及那些虽然相似但大小写不同的元素(例如FoofooBarbar)。

我几天前发了这个问题,得到了一个不错的答案,但在使用更多样本数据进行多次测试后,发现它并不总是按预期工作。

因此,在进一步搜索后,我找到了levenshtein函数,所以我进行了组合,首先检查是否有完全匹配,如果没有,则尝试找到最接近的匹配。现在,问题在于一些列具有相似的名称,例如Catlevel1Catlevel2、...、Catlevel6。因此,如果缺少Catlevel2,它将与最后一个且最相似的列Catlevel6匹配。

这是我目前的代码:

foreach($all_columns as $i => $val1) {
    $result = null;
    // Search the second array for an exact match, if found
    if(($found = array_search($val1,$_SESSION['found_columns'],true)) !==false) {
        $result = $found; 
    } else {
        // Otherwise, see if we can find a case-insensitive matching string 
        //where the element from $arr2 is found within the one from $arr1
        foreach( $_SESSION['found_columns'] as $j => $val2) {
            if($val1<>'' && $val2<>'') {
                if( stripos( $val1, $val2) !== false ) {
                    $result = $j;
                    break;
                } else {
                    $notfound .= $val1.', ';
                    break;
                }
            }
        }
    }
    $_SESSION['found_column_positions'][$i] = $result;
}

/*****ALTERNATIVE METHOD USING levenshtein*****/
$i=0;
foreach($all_columns as $key => $value) {
    $found = wordMatch($value, $arr2, 2);
    $pos = array_search($found, $_SESSION['found_columns']); 
    $_SESSION['found_column_positions'][$i] = $pos;
    $i++;
}

function wordMatch($input, $array, $sensitivity){
    $words = $array;
    $shortest = -1;
    foreach ($words as $word) {
        $lev = levenshtein($input, $word);
        if ($lev == 0) {
            $closest = $word;
            $shortest = 0;
            break;
        }
        if ($lev <= $shortest || $shortest < 0) {
            $closest  = $word;
            $shortest = $lev;
        }
    }
    if($shortest <= $sensitivity){
        return $closest;
    } else {
        return 0;
    }
}


有没有更好的方法比较两个数组,找到最接近的匹配值并将匹配值的键保存到第三个数组中,以用作两个数组之间的键参考?


1
http://in3.php.net/array_diff这是一个PHP函数,用于比较两个或多个数组之间的差异,并返回一个包含所有不同元素的新数组。http://in3.php.net/manual/en/function.array-diff-assoc.php这个PHP函数与array_diff()类似,但它还会比较数组中的键。只有在两个数组中都存在的键值对才会被保留在结果数组中。 - abhij89
2
这需要完全自动化吗?因为我的想法是创建一个小的“向导”,让用户指定哪一列是哪一列,然后只需保存用户输入的映射即可。 - Wolfman Joe
3
你正在进行模糊匹配,因此你会得到一些错误的匹配结果;这实际上就是模糊匹配的意义所在。除非你能制定硬性规则,比如“不接受只有数字最后字符不同的匹配”,否则你将不得不手动挑选可疑的匹配结果。但是请问自己:你真的想要尝试考虑每个人可能搞乱列名的方法吗? - Quinn Strahl
3
我同意前两条评论。你可以编写脚本自动化处理过程,但有一些限制是您不想尝试的。设定一些规则,例如对于catlevel问题,即使是人类也很难确定其所属领域。您需要在数据源正确性和自动化之间找到平衡点。如果您通过不同来源获取这些文件,请不要承担全部责任并保持严格,否则几周后您将不得不为您的脚本打上成百上千个异常补丁。 - aleation
1
@bikey77 如果 $arr1 = array('Desc1', 'Desc2')$arr2 = array('Desc')。这将容易出现错误。 - Ejaz
显示剩余10条评论
6个回答

1
以下脚本将完成工作。
<?php
$arr1 = array('Action', 'LotSize', 'QuantityMinimum', 'SupplierName', 'SPN',
 'PartNumExt', 'UOM', 'ListPrice', 'MPN', 'MFrName', 'CatLevel1', 'CatLevel2',
 'CatLevel3', 'CatLevel4', 'CatLevel5', 'CatLevel6', 'AcctLevel1', 'AcctLevel2', 
 'AcctLevel3', 'AcctLevel4', 'AcctLevel5', 'AcctLevel6', 'Desc1', 'Desc2', 'PicName',
 'SupplierURL', 'CatPart','TechSpec', 'Kad');    

$arr2 = array('Action', 'LotSze', 'QuantityMinimum', 'SupplierName', 'SPN',
 'PartNumEx', 'UOM', 'ListPric', 'MPN', 'MfrName', 'CatLevel1', 'CatLevel2',
 'CatLevel3', 'CatLevel4', 'AcctLevel1', 'AcctLevel2', 'AcctLevel3', 'AcctLevel4',
 'Desc1', 'Desc2', 'PicName', 'SupplierURL', 'CatPart');

$arr3 = array();

foreach($arr1 as $key=>$val)
{
    $arr3[$key] = array_search($val, $arr2);
}

print_r($arr3);
?>

0

使用内部和外部循环的概念来完成此任务。

<pre>
$arr1 = $ar1;
$arr2 = $ar2;
$matching_vals = '';
foreach($arr1 as $array1){
$val_arr1 = $array1;
foreach($arr2 as $array2){
$val_arr2 = $array1;
if($val_arr2 == $val_arr1){
$matching_vals .= $val_arr2;
}
}
}

echo '匹配的值为:'. $matching_vals;

这个概念是将一个数组在循环中迭代,并与第二个数组的每个值进行比较。这是实现此操作的算法。


0
<?php
$arr1 = array('Action', 'LotSize', 'QuantityMinimum', 'SupplierName', 'SPN',
'PartNumExt', 'UOM', 'ListPrice', 'MPN', 'MFrName', 'CatLevel1', 'CatLevel2',
'CatLevel3', 'CatLevel4', 'CatLevel5', 'CatLevel6', 'AcctLevel1', 'AcctLevel2', 
'AcctLevel3', 'AcctLevel4', 'AcctLevel5', 'AcctLevel6', 'Desc1', 'Desc2', 'PicName',
'SupplierURL', 'CatPart','TechSpec', 'Kad');  

$arr2 = array('Action', 'LotSze', 'QuantityMinimum', 'SupplierName', 'SPN',
'PartNumEx', 'UOM', 'ListPric', 'MPN', 'MfrName', 'CatLevel1', 'CatLevel2',
'CatLevel3', 'CatLevel4', 'AcctLevel1', 'AcctLevel2', 'AcctLevel3', 'AcctLevel4',
'Desc1', 'Desc2', 'PicName', 'SupplierURL', 'CatPart');

$arr3=array();

for($i = 0; $i < count($arr1) ;$i++)
{ 
$max=0;
$result=0;
for ($j=0; $j< count($arr2) ; $j++)
{
     $percent=find($arr1[$i],$arr2[$j]);
     if ($percent>$max) 
             { $max= $percent;
               $result=$j; 
            } 

 }
 $arr3[$i]=$result;
}
for($i = 0; $i < count($arr3) ;$i++)
    echo "[".$i."]=> ".$arr3[$i].", ";

function find($a,$b)
{
similar_text($a, $b, $percent);
return $percent;
}
?>

这是完整的程序。希望能对您有所帮助!


0
?php
$arr1 = array('Action', 'LotSize', 'QuantityMinimum', 'SupplierName', 'SPN',
'PartNumExt', 'UOM', 'ListPrice', 'MPN', 'MFrName', 'CatLevel1', 'CatLevel2',
'CatLevel3', 'CatLevel4', 'CatLevel5', 'CatLevel6', 'AcctLevel1', 'AcctLevel2', 
'AcctLevel3', 'AcctLevel4', 'AcctLevel5', 'AcctLevel6', 'Desc1', 'Desc2', 'PicName',
'SupplierURL', 'CatPart','TechSpec', 'Kad');  

$arr2 = array('Action', 'LotSze', 'QuantityMinimum', 'SupplierName', 'SPN',
'PartNumEx', 'UOM', 'ListPric', 'MPN', 'MfrName', 'CatLevel1', 'CatLevel2',
'CatLevel3', 'CatLevel4', 'AcctLevel1', 'AcctLevel2', 'AcctLevel3', 'AcctLevel4',
'Desc1', 'Desc2', 'PicName', 'SupplierURL', 'CatPart');

$arr3 = array_intersect($arr1, $arr2));
echo arr3 ;

?>

0

我不确定这对您是否可行,但我们有一个类似的系统,用户可以管理页面告诉我们每个标头的可能值是什么。然后系统会根据用户创建的映射将其转换为标准接受的标头,然后再尝试上传。这避免了强制用户每次都进行映射。如果他们正在尝试上传的列中没有条目,则上传会抛出错误。

祝您好运!


0
一个非常简单的算法是使用array_search()函数:
$mapping = [];

foreach ($arr1 as $i => $value)
{
    $actualKey = array_search($value, $arr2);

    if (false !== $actualKey)
    {
        $mapping[$i] = $actualKey;
    }
}

array_search($needle, $haystack)

array_search — 在数组中搜索给定的值,如果成功则返回相应的键 [否则返回false]


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