Мне нужно определить unique urls
из массива.
Все следующие варианты должны считаться равными:
http://google.com
https://google.com
http://www.google.com
https://www.google.com
www.google.com
google.com
У меня есть следующее решение:
public static function array_unique_url(array $array) : array
{
$uniqueArray = [];
foreach($array as $item) {
if(!self::in_array_url($item, $uniqueArray)){
$uniqueArray[] = $item;
}
}
return $uniqueArray;
}
public static function in_array_url(string $needle, array $haystack): bool {
$haystack = array_map([self::class, 'normalizeUrl'], $haystack);
$needle = self::normalizeUrl($needle);
return in_array($needle, $haystack);
}
public static function normalizeUrl(string $url) {
$url = strtolower($url);
return preg_replace('#^(https?://)?(www.)?#', '', $url);
}
Однако это не очень эффективно O (n ^ 2). Кто-нибудь может указать мне на лучшее решение?
in_array это дорого. Вместо этого создайте хеш и сохраните значения как их значения.
Что-то вроде:
$myHash = []; //a global array to hold values.
И во время проверки сделайте следующее:
if(!empty($myHash[$needle] )){
//already exits
}
Я не проверял это, но возможно что-то вроде этого будет работать:
function getUniqueUrls(array $urls)
{
$unique_urls = [];
foreach ($urls as $url) {
$normalized_url = preg_replace('#^(https?://)?(www.)?#', '', strtolower($url));
$unique_urls[$normalized_url] = true;
}
return array_keys($unique_urls);
}
$arr = [
'http://google.com',
'https://google.com',
'http://www.google.com',
'https://www.google.com',
'www.google.com',
'google.com'
];
$unique_urls = getUniqueUrls($arr);
Вот упрощенная версия. Он не использует preg_replace, так как стоит дорого. Также это не делает никакой ненужной строковой операции.
$urls = array(
"http://google.com",
"https://google.com",
"http://www.google.com",
"https://www.google.com",
"www.google.com",
"google.com");
$uniqueUrls = array();
foreach($urls as $url) {
$subPos = 0;
if(($pos = stripos($url, "://")) !== false) {
$subPos = $pos + 3;
}
if(($pos = stripos($url, "www.", $subPos)) !== false) {
$subPos = $pos + 4;
}
$subStr = strtolower(substr($url, $subPos));
if(!in_array($subStr, $uniqueUrls)) {
$uniqueUrls[] = $subStr;
}
}
var_dump($uniqueUrls);
Другая оптимизация производительности может заключаться в реализации бинарного поиска по уникальным URL-адресам, поскольку «in_array» выполняет поиск по всему массиву, поскольку он не отсортирован.
<?php
$urls = [
'http://google.com',
'https://google.com',
'http://www.google.com',
'https://www.google.com',
'www.google.com',
'google.com',
'testing.com:9200'
];
$uniqueUrls = [];
foreach ($urls as $url) {
$urlData = parse_url($url);
$urlHostName = array_key_exists('host',$urlData) ? $urlData['host'] : $urlData['path'];
$host = str_replace('www.', '', $urlHostName);
if(!in_array($host, $uniqueUrls) && $host != ''){
array_push($uniqueUrls, $host);
}
}
print_r($uniqueUrls);
?>
почему вы каждый раз нормализуете свой массив результатов?
Вот лучшее решение с вашим кодом:
public static function array_unique_url(array $array): array
{
$uniqueArray = [];
foreach ($array as $item) {
if (!isset($uniqueArray[$item])) {
$uniqueArray[$item] = self::normalizeUrl($item);
}
}
return $uniqueArray;
}
public static function normalizeUrl(string $url)
{
return preg_replace('#^(https?://)?(www.)?#', '', strtolower($url));
}
Когда вы хотите, чтобы ваши оригинальные предметы вы можете использовать array_keys(array_unique_url($array))
для ваших нормализованных URL вам не нужно array_keys
Попробуйте это самое простое решение. Здесь мы используем две функции preg_replace
а также parse_url
для достижения желаемого результата
Попробуйте этот фрагмент кода здесь
<?php
$urls = array(
"http://google.com",
"https://google.com",
"http://www.google.com",
"https://www.google.com",
"www.google.com",
"google.com");
$uniqueUrls=array();
foreach($urls as $url)
{
$changedUrl= preg_replace("/^(https?:\/\/)?/", "http://", $url);//adding http to urls which does not contains.
$domain= preg_replace("/^(www\.)?/","",parse_url($changedUrl,PHP_URL_HOST));//getting the desired host and then removing its www.
preg_match("/^[a-zA-Z0-9]+/", $domain,$matches);//filtering on the basis of domains
$uniqueUrls[$matches[0]]=$domain;
}
print_r(array_values($uniqueUrls));