获取key低于$ i的最后一个数组对象? PHP / Laravel

时间:2017-12-13 17:31:40

标签: php laravel

您好我试图抓住一个数组中的最后一个对象,其中键在for循环中低于$ i。

原因是因为数组键是unix时间,我需要抓住最接近当前时间的元素,在我的情况下是$ i。

如何在PHP或laravel中实现这一目标?

基本上这是因为我正在创建一个图表,我在包含数据日期的数组之间添加了错过的日期。

对于每个丢失的日期,它应使用当前日期数据之前的日期。

我希望它有意义。

编辑1:

我尝试使用的是laravels array_first helper:

$first = array_first($array, function ($value, $key) use ($i) {
    return $key <= $i;
});

但是这没用。 我也尝试了phps end()函数,它获取了数组中的最后一个对象,但是这也没有用。

以下是整个功能的代码:

public function getPortfolioHistory($days, $currency, $type)
{
  if(count($this->getHoldingLogs()) > 0)
  {

   //$history = Cache::remember('1213:'.$this->id, 1440, function() use ($days, $currency){
  $logs = $this->getHoldingLogs(); // Get all logs
  $fromdate = strtotime(\Carbon\Carbon::today()->subDays($days)); // Gets the last day it should get prices from
  $array = array(); // Array that handles dates
  $token = array(); // Array that handles token amounts for calculations
  $cmc_id = array(); //Array that handles coinmarketcap ids
  $pricelog = array(); //Array that handles coinmarketcap historical prices
  $paid_usd = 0;

  foreach($logs as $log)
  {

    // This block checks if the holdinglogs date is older than the fromdate, it should set its date to the $fromdate (starting date)
    if(strtotime($log->date) < $fromdate)
    {
      $log->date = date('Y-m-d H:i:s', $fromdate);
    }

    // Just a variable for resetting $worth_usd for each $log
    $worth_usd = 0;
    $paid_usd = 0;
    // Reformat the date
    $date = date('Y-m-d', strtotime($log->date));

    // Checks if this token has been set or not in the $token array, if it has been set we update its amount.
    if(!isSet($token[$log->token]))
    {
      $token[$log->token] = array('token' => $log->token, 'amount' => $log->amount, 'cmc_id' => $log->token_cmc_id, 'paid' => $log->paid_usd);
    } else {
      if($log->paid_usd == 0 && $log->amount < 0)
      {
        $log->paid_usd = 0 - (($token[$log->token]['paid'] / $token[$log->token]['amount']) * (abs($log->amount)));
        $log->save();
      }
      $token[$log->token] = array('token' => $log->token, 'amount' => $token[$log->token]['amount'] + $log->amount, 'cmc_id' => $log->token_cmc_id, 'paid' => $token[$log->token]['paid'] + $log->paid_usd);
    }


    // Since we need to recalculate using the amounts each day, we loop through the tokens in $token
    foreach($token as $key => $coin)
    {

      // This checks if we have already read in the price array for this token, if it has not been set we get it.
      if(!isSet($pricelog[$coin['cmc_id']]) && $coin['cmc_id'] != "FIAT")
      {
        $pricelog[$coin['cmc_id']] = $this->getHistorical($coin['cmc_id']);
      }

      // If the token is a fiat we set its price to 1 and then we multiply the amount * price * fiat later in the code.
      if($coin['cmc_id'] != "FIAT")
      {
        $price = $this->returnPrice($pricelog[$coin['cmc_id']], $log->date);
      } else {
        $price = 1;
      }

      // Here we calculate the worth.
      if($coin['cmc_id'] != "FIAT")
      {
        $worth_usd += $coin['amount'] * $price['USD'];
      } else {
        if($key == "USD")
        {
          $worth_usd += $coin['amount'];
        } else {
          $worth_usd += $coin['amount'] / (1 / Multiplier::where('currency', $coin['token'])->select('price')->first()->price); // If its a fiat and its not USD we convert it to usd.
        }
      }
      $paid_usd += $coin['paid'];
    }

    // Sets the record in the array with all the needed data.
    $array = array_set($array, strtotime($date), array('worth_usd' => $worth_usd, 'tokens' => $token, 'date' => $date, 'paid' => $paid_usd, 'generated' => "no"));
  }



  // Sort the array by key.
  ksort($array);

  // This is the method depixel wrote to add missing dates.
  for($i = key($array); $i <= strtotime(date('Y-m-d')); $i += 86400)
  {

    if(isSet($array[$i]) == false)
    {
      // This gets the record before $i, so we can read in the latest holdings.
      $latest = end($array);

      echo date('Y-m-d', $i) . " - " . $latest['date'] . "<br>";

      // Set worth_usd to 0.
      $worth_usd = 0;
      $paid_usd = 0;

      // Formats the $i unix time to a suitable date.
      $missingdate = date('Y-m-d', $i);

      foreach($latest['tokens'] as $key => $coin)
      {

      if(!isSet($pricelog[$coin['cmc_id']]) && $coin['cmc_id'] != "FIAT")
      {
        $pricelog[$coin['cmc_id']] = $this->getHistorical($coin['cmc_id']);
      }
      if($coin['cmc_id'] != "FIAT")
      {
        $price = $this->returnPrice($pricelog[$coin['cmc_id']], $missingdate);
      } else {
        $price = 1;
      }

      // Worth of this holding this day
      if($coin['cmc_id'] != "FIAT")
      {
        $worth_usd += $coin['amount'] * $price['USD'];
      } else {
        if($key == "USD")
        {
          $worth_usd += $coin['amount'];
        } else {
          $worth_usd += $coin['amount'] / (1 / Multiplier::where('currency', $coin['token'])->select('price')->first()->price);
        }
      }
      $paid_usd += $coin['paid'];

    }
    if($missingdate != date('Y-m-d'))
    {
      $array = array_set($array, $i, array('worth_usd' => $worth_usd, 'tokens' => $token, 'date' => $missingdate, 'paid' => $paid_usd, 'generated' => 'yes'));
    }
    }
  }
  // Sort array by time
  ksort($array);
  // Print it out
  $history = $array;
//  });

  echo "<pre>";
  echo var_dump($history);
  echo "</pre>";

  foreach($history as $worth)
  {
    if($type == "Value")
    {
      echo $worth['worth_usd'].", ";
    } else {
      echo $worth['worth_usd'] - $worth['paid'],", ";
    }
  }
}
}

编辑3:

我实际上是通过使用它解决了它:

      $latest = null;
      foreach ($array as $key => $date) {
         if ($latest === null || $key < $i && $key > key($latest)) {
            $latest = $date;
         }
      }

但如果有人有更好的方法,请告诉我。

1 个答案:

答案 0 :(得分:0)

(旁注:你的解决方案实际上不起作用,因为它有一个很大的错误:key($latest)表示“在名为$latest的数组的当前索引处获取密钥的值” - 但$latest不是您正在搜索的数组,也不知道它来自$array中的键。)

你正在排序$array,这很好,但是有一个重要的好处就是让你错过了:能够进行二分搜索。

不幸的是,由于你的数组键不是连续的和从零开始的(即实际索引),二进制搜索会有点困难。所以我会改变这一行(顺便说一句,你在这里使用array_set()是完全没必要的):

$array = array_set($array, strtotime($date), array('worth_usd' => $worth_usd, 'tokens' => $token, 'date' => $date, 'paid' => $paid_usd, 'generated' => "no"));

到此:

$array[] = ['date' => $date, 'time' => strtotime($date), 'worth_usd' => $worth_usd, 'tokens' => $token, 'paid' => $paid_usd, 'generated' => 'no'];

现在您的数组键只是索引,因此您可以执行二进制搜索以查找当前日期之前的最新条目的索引:

$lbound = 0;
$rbound = count($array) - 1;
$current_time = time(); // or whatever timestamp you're looking for

while($rbound - $lbound > 1) {
    $midpoint = $lbound + ($rbound - $lbound) / 2;

    if ($array[$midpoint]['time'] <= $current_time) {
        $lbound = $midpoint;
        continue;
    }
    $rbound = $midpoint;
}

if ($array[$lbound]['time'] === $current_time) {
    $lbound--;
}
// Now $lbound is the index of the most recent entry before the current time
// -- unless the first element in the array is the current time, in which
// case $lbound will be -1, which would represent failure or "not found".
// However, even in that case, the insertion point for the new entry
// would still be $lbound + 1.

// So you would insert a new entry like this:
//     array_splice($array, $lbound + 1, 0, [$new_entry]);
// don't forget the [ ] around that array variable!

尚未测试该代码,所以希望它是正确的。唯一需要注意的是,这段代码不适用于只有两个元素的数组(但你甚至不需要对那些小的元素进行二进制搜索)。

[编辑] 如果你需要考虑一个2元素的数组,你可以使用下面的代码,虽然由于额外的if而不是那么高效:

$lbound = $midpoint = 0;
$rbound = count($array) - 1;
$current_time = time(); // or whatever timestamp you're looking for

while($lbound <= $rbound) {
    $midpoint = $lbound + ($rbound - $lbound) / 2;

    if ($array[$midpoint]['time'] === $current_time) {
        break;
    }
    if ($array[$midpoint]['time'] < $current_time) {
        $lbound = $midpoint + 1;
        continue;
    }
    $rbound = $midpoint - 1;
}

if ($array[$midpoint]['time'] >= $current_time) {
    $midpoint--;
}

// and in this case, $midpoint will hold the position of the most recent
// entry before the current time -- unless the first element in the array
// is the current time, in which case $midpoint will be -1, which would
// represent failure or "not found".
// However, even in that case, the insertion point for the new entry
// would still be $midpoint + 1.

// So you would insert a new entry like this:
//     array_splice($array, $midpoint + 1, 0, [$new_entry]);
// don't forget the [ ] around that array variable!