Помогите извлечь данные из массива PHP на основе 5 правил - PullRequest
0 голосов
/ 20 апреля 2009

Я работаю с массивами путей к файлам изображений. Типичный массив может содержать 5 путей к файлам изображений.

Для каждого массива я хочу вытащить только «лучшую» фотографию для отображения в качестве эскиза для коллекции.

Я нахожу циклы и массивы очень запутанными, и после 4 часов попыток выяснить, как их структурировать, я в растерянности.

Вот правила, с которыми я работаю:

  1. Самые лучшие фотографии имеют "-large" в своих путях к файлам. Не во всех массивах будут такие изображения, но если они есть, это всегда фотография, которую я хочу вытащить.

  2. Следующие лучшие фотографии имеют ширину 260 пикселей. Я могу посмотреть на это с помощью getimagesize. Если я найду один из них, я хочу перестать искать и использовать его.

  3. Следующие лучшие фотографии имеют ширину 265. Если я найду один, я хочу использовать его и перестану искать.

  4. Следующие лучшие фотографии имеют ширину 600 пикселей. То же самое дело.

  5. Тогда ширина 220px.

Мне нужно 5 отдельных петель? 5 вложенных циклов

Вот что я пытаюсь:

if $image_array{    
  loop through $image_array looking for "-large"
  if you find it, print it and break;             

  if you didn't find it, loop through $image_array looking for 260px wide.
  if you find it, print it and break;
}

и т. Д.

Но, похоже, это не работает.

Я хочу "найти" в моем массиве лучшее единственное изображение на основе этих критериев. Если он не может найти первый тип, он ищет второй и так далее. Как это сделано?

Ответы [ 5 ]

0 голосов
/ 20 апреля 2009

Вам нужно 3 цикла и выбор по умолчанию.

loop through $image_array looking for "-large"
if you find it, return it;

if you didn't find it, loop through $image_array
get image width
if prefered width (260px), return it.
if $sizes[$width] not set, add filename

loop a list of prefered sizes in order and see if it is set in $sizes
if you find it, return it;

return the first image or default image;
0 голосов
/ 20 апреля 2009

Другой подход (тривиальный, менее общий, медленный). Просто проверьте правила по одному:

function getBestFile($files) {
    foreach ($files as $arrayKey => $file) {
        if (strstr($file, '-large') !== FALSE) {
            return $file;
        }
    }
    foreach ($files as $arrayKey => $file) {
        if (is260wide($file)) {
            return $file;
        }
    }
    // ...
}
0 голосов
/ 20 апреля 2009
<?php

// decide if 1 or 2 is better
function selectBestImage($image1, $image2) {
    // fix for strange array_filter behaviour
    if ($image1 === 0)
        return $image2;

    list($path1, $info1) = $image1;
    list($path2, $info2) = $image2;
    $width1 = $info1[0];
    $width2 = $info2[0];

    // ugly if-block :(
    if ($width1 == 260) {
        return $image1;
    } elseif ($width2 == 260) {
        return $image2;
    } elseif ($width1 == 265) {
        return $image1;
    }  elseif ($width2 == 265) {
        return $image2;
    } elseif ($width1 == 600) {
        return $image1;
    }  elseif ($width2 == 600) {
        return $image2;
    } elseif ($width1 == 220) {
        return $image1;
    }  elseif ($width2 == 220) {
        return $image2;
    } else {
        // nothing applied, so both are suboptimal
        // just return one of them
        return $image1;
    }
}

function getBestImage($images) {
    // step 1: is the absolutley best solution present?
    foreach ($images as $key => $image) {
        if (strpos($image, '-large') !== false) {
            // yes! take it and ignore the rest.
            return $image;
        }
    }

    // step 2: no best solution
    // prepare image widths so we don't have to get them more than once
    foreach ($images as $key => $image) {
        $images[$key] = array($image, getImageInfo($image));
    }

    // step 3: filter based on width
    $bestImage = array_reduce($images, 'selectBestImage');

    // the [0] index is because we have an array of 2-index arrays - ($path, $info)
    return $bestImage[0];
}

$images = array('image1.png', 'image-large.png', 'image-foo.png', ...);

$bestImage = getBestImage($images);

?>

это должно работать (я не проверял это), но это неоптимально.

как это работает? во-первых, мы ищем абсолютно лучший результат, в данном случае -large, потому что поиск подстрок недорог (в сравнении).

если мы не найдем -large изображение, мы должны проанализировать ширину изображения (дороже! - поэтому мы предварительно рассчитаем их).

array_reduce вызывает функцию фильтрации, которая принимает 2 значения массива и заменяет эти два на одно возвращаемое функцией (лучшее). это повторяется до тех пор, пока в массиве не останется только одно значение.

это решение все еще неоптимально, потому что сравнения (даже если они дешевые) выполняются более одного раза. мои навыки обозначения big-O () немного (ха!) ржавые, но я думаю, что это O (n * logn). Решение Soulmerges лучше - O (n):)

Вы все еще можете улучшить решение Soulmerges, потому что второй цикл не нужен:

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

<?php

function getBestImage($images) {
    $highestScore = 0;
    $highestPath  = '';

    foreach ($images as $image) {
        if (strpos($image, '-large') !== false) {
            return $image;
        } else {
            list($width) = getImageInfo($image);

            if ($width == 260 && $highestScore < 5) {
                $highestScore = 5;
                $highestPath  = $image;

            } elseif ($width == 265 && $highestScore < 4) {
                $highestScore = 4;
                $highestPath  = $image;

            } elseif ($width == 600 && $highestScore < 3) {
                $highestScore = 3;
                $highestPath  = $image;

            } elseif ($width == 220 && $highestScore < 2) {
                $highestScore = 2;
                $highestPath  = $image;
            } elseif ($highestScore < 1) {
                // the loser case
                $highestScore = 1;
                $highestPath  = $image;
            }
        }
    }

    return $highestPath;
}

$bestImage = getBestImage($images);

?>

не проверял, должен работать в O (n). не могу представить более быстрый и эффективный способ банкомата.

0 голосов
/ 20 апреля 2009
// predefined list of image qualities (higher number = best quality)
// you can add more levels as you see fit
$quality_levels = array(
    260 => 4, 
    265 => 3, 
    600 => 2,
    220 => 1
);


if ($image_arry) {

    $best_image = null;

    // first search for "-large" in filename 
    // because looping through array of strings is faster then getimagesize
    foreach ($image_arry as $filename) {
          if (strpos('-large', $filename) !== false) {
                $best_image = $filename;
                break;
            }
    }

    // only do this loop if -large image doesn't exist
    if ($best_image == null) {
            $best_quality_so_far = 0;

        foreach ($image_arry as $filename) {
            $size = getimagesize($filename);
            $width = $size[0];

                    // translate width into quality level
            $quality = $quality_levels[$width];

            if ($quality > $best_quality_so_far) {
                $best_quality_so_far = $quality;
                $best_image = $filename;
            }
        }
    }

    // we should have best image now
    if ($best == null) {
        echo "no image found";
    } else {
        echo "best image is $best";
    }
}
0 голосов
/ 20 апреля 2009

Я бы назначил очки файлам в зависимости от того, сколько правил применяется. Если вы хотите, чтобы определенные правила вытесняли другие, вы можете дать больше очков за это правило.

define('RULE_POINTS_LARGE', 10);
define('RULE_POINTS_260_WIDE', 5);
// ...

$points = array();
foreach ($files as $arrayKey => $file) {
    $points[$arrayKey] = 0;
    if (strstr($filename, '-large') !== FALSE) {
        $points[$arrayKey] += RULE_POINTS_LARGE;
    }
    // if ...
}

// find the highest value in the array:
$highestKey = 0;
$highestPoints = 0;
foreach ($points as $arrayKey => $points) {
    if ($files[$arrayKey] > $highestPoints) {
        $highestPoints = $files[$arrayKey];
        $highestKey = $arrayKey;
    }
}

// The best picture is $files[$highestKey]

Еще одно примечание: если вы зададите кратные значения для ваших правил, это обеспечит, что правило может быть «сильнее», чем все остальные. Пример: 5 правил -> значения правил (1, 2, 4, 8, 16).

  • 1 <2 </li>
  • 1 + 2 <4 </li>
  • 1 + 2 + 4 <8 </li>
  • и т.д.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...