PHP 按时间排序数组并找到与 X 数字最接近的匹配项

Posted

技术标签:

【中文标题】PHP 按时间排序数组并找到与 X 数字最接近的匹配项【英文标题】:PHP Sorting Array by Time & finding the closest matches to X numbers 【发布时间】:2018-03-16 06:11:45 【问题描述】:

我的目标不是将任何项目保存到数据库,而只是显示实时流。

我正在从赫芬顿邮报获取 RSS 提要

http://www.huffingtonpost.com/section/front-page/feed

我有一个包含 Huff 最近 50 篇文章的 WordPress 数组 (php)。

$rss = fetch_feed($feed_url);

我希望我的 RSS 提要每天仅显示 X 的唯一帖子总数。为了简单起见,我只是要显示最接近 24 / X 间隔的帖子。

为了演示,让我们使用 3。Feed 会吐出最接近 8 点、16 点(下午 2 点)和 24 点(午夜)或(0、8 和 16 点)发布的帖子。

在 PHP 中,如何通过发布的时间变量对对象数组进行排序,然后找到最接近该时间的帖子?现在我正在做一个非常迂回的方式,目前甚至无法正常工作。

这是我目前的逻辑:

if(function_exists('fetch_feed')) 

    $rss = fetch_feed(get_field('feed_url'));
    if(!is_wp_error($rss)) : // error check
        $maxitems = $rss->get_item_quantity(50); // number of items at 50
        $rss_items = $rss->get_items(0, $maxitems);
    endif;
    // display feed items ?>
    <h1><?php echo $rss->get_title(); ?></h1>
<?php
$coutner = 0;
$daily_max = 3; //how many unique feeds to display per day
$display_interval = floor(24 / $daily_max); //simple way to make even intervals
$posting_time = array(); //to store the times to post
foreach(range(0, $daily_max-1) as $i) 
    $posting_time[$i] = $display_interval * $i;


$post_interval = 0;
$date = new DateTime();
$today = date("G"); //getting the current day's hour

$time_adjust = $today / $display_interval;

//adjust the posting times order so that its circular
while($today > $posting_time[0])
$hold = array_pop($posting_time);
echo '<p>hold: ' . $hold;
array_unshift($posting_time,$hold);

$accessing = array_pop($posting_time);
?>
    <dl>
    <?php if($maxitems == 0) echo '<dt>Feed not available.</dt>';

else 

foreach ($rss_items as $item) : ?>

<?php
 //as soon as the first item is newer than post time, output it & count that time slot as being filled
$rss_item_hour = $item->get_date('G');
if($rss_item_hour > $accessing) ?>
        <dt>
            <a href="<?php echo $item->get_permalink(); ?>" 
            title="<?php echo $item->get_date('j F Y @ G'); ?>">
            <?php echo $item->get_title(); ?>
            </a>
        </dt>
        <dd>
            <?php echo $item->get_description(); ?>
        </dd>
<p>
<?php echo $item->get_date('j F Y | G'); 
?>
</p>
<?php $coutner = $coutner + 1;
$accessing = array_pop($posting_time);
                                
elseecho '<p>else'; ?>

    <?php endforeach; ?>
    </dl>
<?php  ?>

目前的主要错误是,有时while($today &gt; $posting_time[0]) 的循环移动会无限进行。而且循环似乎永远不会按计划进行。

【问题讨论】:

您是正确的,while 循环是问题,因为您从未在循环内更新 $today。如果它是真的,它将永远是真的,因为它永远不会被设置为其他任何东西。这绝对看起来过于复杂。我会尝试制定解决方案。 您是否真正关心显示的帖子在一天中的间隔是否均匀,或者只是为了简化它(提示:不关心要简单得多) ?从提要开始,您每天需要 3 件物品,还是过去 24 小时内只需要 3 件?是否保证每天/过去 24 小时内始终至少有 3 件商品? 那么,如果提要在凌晨 1 点检索,应该显示什么?由于下一个“里程碑”是上午 8 点,这意味着“最近的 4 个帖子”直到上午 8 点?那么,随着时间的推移,显示的帖子正在发生变化,因为可能会有更新的帖子“更接近”某个里程碑?阅读此内容,然后重新考虑您的问题,我会说xyproblem.info 既然您想要每 8 小时时间间隔(或 6 小时等...)的提要,您现在不应该只获取最后 x 小时的提要吗? 【参考方案1】:

试试下面的例子,file_get_contents 用于这个例子来获取xml。它将获取过去 8 小时内的所有提要。尝试使用DOMDocument 来处理xml 提要和Datetime 来管理所需的时间比较。

$hour_interval = 8;

$feeds = file_get_contents("http://www.huffingtonpost.com/section/front-page/feed");
$doc = new DOMDocument();
$doc->loadXML($feeds);
$items = $doc->getElementsByTagName('item');

$today = new DateTime("now",new DateTimeZone("-04:00")); // do mind the timezone it is the one set in the xml feeds so it is needed for correct time comparison
$nowTimestamp = $today->getTimestamp();
$today->modify('-'.$hour_interval.' hour');
$eightHoursBeforeTimestamp = $today->getTimestamp();

$lastEightHoursItems = [];

foreach ($items as $item) 
    $pubDate = $item->getElementsByTagName('pubDate')[0]->nodeValue;
    $feedDate = new DateTime($pubDate);
    $feedTimestamp = $feedDate->getTimestamp();
    if($feedTimestamp<=$nowTimestamp and $feedTimestamp>=$eightHoursBeforeTimestamp) 
        array_push($lastEightHoursItems,$item);
    

$random_keys = array_rand($lastEightHoursItems,3);
$c = count($random_keys);
for($i=0;$i<$c;$i++) 
    echo $lastEightHoursItems[$random_keys[$i]]->getElementsByTagName('title')[0]->nodeValue;
    echo $lastEightHoursItems[$random_keys[$i]]->getElementsByTagName('link')[0]->nodeValue;
    echo $lastEightHoursItems[$random_keys[$i]]->getElementsByTagName('description')[0]->nodeValue;
    echo $lastEightHoursItems[$random_keys[$i]]->getElementsByTagName('pubDate')[0]->nodeValue;

【讨论】:

您的代码看起来最简洁,所以我一直在尝试对其进行测试,但它并不限制显示的提要项目的数量。我真的不在乎显示什么内容,或者它与时间完美协调,只要提要“精简”内容总量并保持一致。 呃,我才意识到为什么这不起作用。 RSS 提要有问题,所以当它在中间跳过多个项目时,我认为你的工作方式与实际情况不同。你的在 8 小时内抓住一切。我正在尝试做的是限制在我的提要中显示的项目数量。我想要每天最多 X 篇文章。我任意设置了 3 的最大值,这意味着我希望 Feed 在 24 小时内显示 3 篇新文章。这意味着“均匀”的做法是将 24 小时除以 3 分为 8 小时段,并每天取最接近 8 小时标记的项目。 @jonbon 编辑了我的答案,因此每次刷新页面时,您都会在过去 8 小时内获得 3 篇随机文章【参考方案2】:

将时间视为“一天中的秒数”(0 - 86400),以下几行将满足您的需求(简化示例):

<?php

$postTimes = array(1,600,953,1900,23500,27600,56000,72000);

echo "Closest match is: " + findMatch(24000, $postTimes); //23500

function findMatch($needle, $haystack) 
   $closest = null;
   foreach ($haystack as $element) 
      if ($closest === null || abs($needle - $closest) > abs($element - $needle)) 
         $closest = $element;
      
   
   return $closest;

?>

最后你只需要实现:

 getPostTimesAsSeconds($postArray); //foreach converting dates to seconds-array

 pickPostBySecondsOfTheDay(23500); //foreach, finding the post matching the seconds of the day. 

【讨论】:

【参考方案3】:

我尝试根据您采用的方法构建解决方案,并使其变得更加简单。有许多边缘情况需要考虑,我稍后会解释,但我认为它仍然会按原样实现您应用的基本目标,但我确实做了一些假设。

...
<?php

    $counter = 0;
    $daily_max = 3; //how many unique feeds to display per day
    $display_interval = floor(24 / $daily_max); //simple way to make even intervals
    $posting_time = array(); //to store the times to post

    // Create a list of time intervals largest to smallest ex. [16, 8, 0]
    foreach(range($daily_max-1, 0) as $i) 
        $posting_time[] = $display_interval * $i;
    

?>
    <dl>
        <?php 
            if($maxitems == 0) 
                echo '<dt>Feed not available.</dt>';

            
            else 

                foreach ($rss_items as $item)
                    if(count ($posting_time) == 0)
                        break;
                      

                    //as soon as the first item is older than the most current post time, output it & count that time slot as being filled
                    $rss_item_hour = $item->get_date('G');

                    if($rss_item_hour < $posting_time[0]) 
        ?>

                        <dt>
                            <a href="<?php echo $item->get_permalink(); ?>" 
                                title="<?php echo $item->get_date('j F Y @ G'); ?>">
                                <?php echo $item->get_title(); ?>
                            </a>
                        </dt>
                        <dd>
                            <?php echo $item->get_description(); ?>
                        </dd>

                        <p>
                            <?php echo $item->get_date('j F Y | G'); ?>
                        </p>

        <?php 
                            $counter++;
                            array_pop($posting_time);
                     
                    else
                        // Debug message
                    
                
            

        ?>
    </dl>
...

好的,由于我无权访问您的 fetch_feed 数据,因此未经测试,但如果有错误,我很乐意更新。

这会选择按您指定的时间间隔粗略划分的帖子,但它不会进行任何检查以确定它们与这些边界的接近程度。例如,如果最新的帖子是在 16:01,它将被跳过以支持 16:00 之前的第一个帖子,例如 9:00。然后它会寻找 8:00 之前的第一个帖子,可能是在 7:59,所以你会有两个时间非常接近的帖子。或者如果在 16:00 和 8:00 之间没有帖子,则显示的第一个帖子可能在 7:30,然后下一个帖子,也许在 7:28 也会显示(因为它现在是第一个帖子8) 之前可用。

我的假设是您不太关心确切的间距,而是对稍微“减少”帖子的数量更感兴趣,这应该可以实现并且希望适合您的应用程序。

正如我所说,如果您有特定的想法,我很乐意帮助您完善它。

【讨论】:

以上是关于PHP 按时间排序数组并找到与 X 数字最接近的匹配项的主要内容,如果未能解决你的问题,请参考以下文章

c_cpp 在排序数组中,找到最接近给定数字的数字

如何找到最接近某个数字的数组元素? [复制]

C++:查找数组中最接近的值

Leetcode 658.找到K个最接近的元素

leetcode-658 找到K个最接近的元素

在有序数组中找到小于 x 的最大值