2011-02-05 1 views
2

Я пишу базовый инструмент категоризации, который возьмет заголовок, а затем сравните его с массивом ключевых слов. Пример:php match string для нескольких массивов ключевых слов

$cat['dining'] = array('food','restaurant','brunch','meal','cand(y|ies)'); 
$cat['services'] = array('service','cleaners','framing','printing'); 
$string = 'Dinner at seafood restaurant'; 

Существуют ли творческие способы циклического перехода по этим категориям или для просмотра, какая категория имеет большинство совпадений? Обратите внимание, что в массиве «столовая» у меня есть регулярное выражение, чтобы соответствовать вариациям слова candy. Я попытался следующие, но эти списки категорий уже довольно долго, мне интересно, если это лучший способ:

$keywordRegex = implode("|",$cat['dining']); 
preg_match_all("/(\b{$keywordRegex}\b)/i",$string,$matches]); 

Спасибо, Стив

EDIT: Благодаря @jmathai, я был возможность добавить рейтинг:

$matches = array(); 
    foreach($keywords as $k => $v) { 
     str_replace($v, '#####', $masterString,$count); 
     if($count > 0){ 
      $matches[$k] = $count; 
     } 
    } 
    arsort($matches); 
+0

Не знаю, php слишком хорошо, но я подозреваю, что хэш будет быстрее, чем регулярное выражение. Если у вас есть одно из значений в качестве реального регулярного выражения (например, cand (y | ies), запустите его как регулярное выражение для объекта, например, поместите значения регулярных выражений в отдельный хэш. – sln

ответ

4

Это может быть сделано одним контуром.

Я хотел бы разделить конфеты и конфеты на отдельные записи для повышения эффективности. Умный трюк состоял бы в том, чтобы заменить матчи с помощью некоторого токена. Давайте использовать 10 #.

$cat['dining'] = array('food','restaurant','brunch','meal','candy','candies'); 
$cat['services'] = array('service','cleaners','framing','printing'); 
$string = 'Dinner at seafood restaurant'; 

$max = array(null, 0); // category, occurences 
foreach($cat as $k => $v) { 
    $replaced = str_replace($v, '##########', $string); 
    preg_match_all('/##########/i', $replaced, $matches); 
    if(count($matches[0]) > $max[1]) { 
    $max[0] = $k; 
    $max[1] = count($matches[0]); 
    } 
} 

echo "Category {$max[0]} has the most ({$max[1]}) matches.\n"; 
+0

Чистый гений. Я немного упростил его и добавил количество совпадений в моем отредактированном сообщении. – daxiang28

+0

@ daxiang28: Тем не менее, таким образом, вы должны сопоставлять строку индивидуально против каждого слова в '$ cat', а не использовать быстрый поиск по одному слову в строке, а в вышеприведенном варианте вы получаете только имя первой категории с наибольшим количество совпадающих слов. (Ваше изменение в этом отношении лучше). Кроме того, по-прежнему остается проблема ввода всех вариантов. Умный, хотя. – Orbling

+0

Я мог бы попытаться устранить шаг str_replace, используя маркеры границы слова \ b в вызове preg_match_all. Но более тревожным аспектом этой проблемы является время выполнения решения. По мере увеличения количества элементов в каждой категории, цикл увеличивается на количество слов в слове поиска. Поворот массивов в ассоциативных списках (думаю, Set здесь) будет торговать памятью для выполнения. Тем не менее, я думаю, что существует проблема масштабирования с подходом. Вероятно, это достаточно хорошо для небольших проектов. – jjohn

2
$cat['dining'] = array('food','restaurant','brunch','meal'); 
$cat['services'] = array('service','cleaners','framing','printing'); 
$string = 'Dinner at seafood restaurant'; 

$string = explode(' ',$string); 
foreach ($cat as $key => $val) { 
    $kwdMatches[$key] = count(array_intersect($string,$val)); 
} 
arsort($kwdMatches); 

echo "<pre>"; 
print_r($kwdMatches); 
+0

Это не учитывает регулярные выражения? –

+0

hmm ... ну это не учитывает совпадения с шаблонами/вариациями ... –

0

Вы выполняете O (н * м) поиск по п быть размер вашей категории и м быть размер заголовка. Вы могли бы попробовать организовать их так:

const $DINING = 0; 
const $SERVICES = 1; 

$categories = array(
    "food" => $DINING, 
    "restaurant" => $DINING, 
    "service" => $SERVICES, 
); 

Тогда для каждого слова в названии, проверить $categories[$word] найти категорию - это получает вас O (м).

1

Обеспечение количества слов не слишком велико, поэтому создание таблицы обратного поиска может быть идеей, а затем запустить заголовок против него.

// One-time reverse category creation 
$reverseCat = array();  
foreach ($cat as $cCategory => $cWordList) { 
    foreach ($cWordList as $cWord) { 
     if (!array_key_exists($cWord, $reverseCat)) { 
      $reverseCat[$cWord] = array($cCategory); 
     } else if (!in_array($cCategory, $reverseCat[$cWord])) { 
      $reverseCat[$cWord][] = $cCategory; 
     } 
    } 
} 

// Processing a title 
$stringWords = preg_split("/\b/", $string); 

$matchingCategories = array(); 
foreach ($stringWords as $cWord) { 
    if (array_key_exists($cWord, $reverseCat)) { 
     $matchingCategories = array_merge($matchingCategories, $reverseCat[$cWord]); 
    } 
} 

$matchingCategories = array_unique($matchingCategories); 
+0

Обратите внимание, что если ранжирование было необходимо, то вместо вызова 'array_unique() в конце быстрый цикл O (n) в' $ matchingCategories', чтобы построить таблицу count, а затем 'arsort()' дала бы нисходящее ранжирование. – Orbling

+0

Описывает ли это условия «cand (y | ies)»? – daxiang28

+0

@ daxiang28: Извините, я не заметил этого, если вы просто написал это как «конфеты», «конфеты», тогда это было бы. Вам действительно нужен матч регулярного выражения?Если у вас могут быть произвольные регулярные выражения во всех целевых словах, то я думаю, вам придется сопоставлять каждое слово в '$ string' с каждым словом в подмассивах' $ cat', довольно медленно. – Orbling

0

Хорошо, вот мой новый ответ, который позволяет использовать регулярное выражение в $ кошки [п] значения ... есть только один нюанс этого кода, который я не могу понять ... по какой-то причине, он не если у вас есть какой-то метасимвол или класс символов в начале вашего значения $ cat [n].

Пример: .*food не будет работать. Но s.afood или sea.* etc ... или ваш пример cand(y|ies) будет работать. Я вроде как понял, что это будет достаточно для вас, поскольку я решил, что смысл регулярного выражения состоял в том, чтобы обрабатывать разные времена слов, и начало слов редко меняется в этом случае.

function rMatch ($a,$b) { 
    if (preg_match('~^'.$b.'$~i',$a)) return 0; 
    if ($a>$b) return 1; 
    return -1; 
} 

$string = explode(' ',$string); 
foreach ($cat as $key => $val) { 
    $kwdMatches[$key] = count(array_uintersect($string,$val,'rMatch')); 
} 
arsort($kwdMatches); 

echo "<pre>"; 
print_r($kwdMatches);