I am trying to create a feature which essentially amounts to a Facebook style newsfeed. In order to order this newsfeed, I call and get all the information associated with all existing "stories" and orders them based on time.
However, I want to reorder these "stories" based on the outcome of a subsequent PHP call. How would you suggest that I go about this? I am really lost right now, so anything that could get me within the ball park would be great!
PHP usort might be what you're looking for:
<?php
function cmp($a, $b)
{
if ($a->somefield == $b->somefield) {
return 0;
}
return ($a->somefield < $b->somefield) ? -1 : 1;
}
$feeds = getFeeds();
usort($feeds, "cmp");
?>
http://php.net/manual/en/function.usort.php
Related
Although, I have successfully implemented Google Keyword Planner API to generate Keyword Ideas in PHP with the link below.
https://developers.google.com/google-ads/api/docs/keyword-planning/generate-keyword-ideas
Does anyone know the fastest way to sort the result by AvgMonthlySearches?
// Iterate over the results and print its detail.
foreach ($response->iterateAllElements() as $result) {
/** #var GenerateKeywordIdeaResult $result */
// Note that the competition printed below is enum value.
// For example, a value of 2 will be returned when the competition is 'LOW'.
// A mapping of enum names to values can be found at KeywordPlanCompetitionLevel.php.
printf(
"Keyword idea text '%s' has %d average monthly searches and competition as %d.%s",
$result->getText(),
is_null($result->getKeywordIdeaMetrics()) ?
0 : $result->getKeywordIdeaMetrics()->getAvgMonthlySearches(),
is_null($result->getKeywordIdeaMetrics()) ?
0 : $result->getKeywordIdeaMetrics()->getCompetition(),
PHP_EOL
);
}
Thanks
You could implement a function that can compare two instances of your object and use usort:
function cmp($a, $b) {
return $a->getKeywordIdeaMetrics()->getAvgMonthlySearches() - $b->getKeywordIdeaMetrics()->getAvgMonthlySearches();
}
$list = iterator_to_array($response->->iterateAllElements());
usort($list, "cmp");
// $list will be your sorted array to work with from here onwards ...
See more:
Sort array of objects by object fields
https://www.php.net/manual/en/function.iterator-to-array.php
I have this simple foreach call that outputs a list of components and is working fine.
foreach ($config["components"] as $component_index => $component) {
echo '<li>$component['name']</li>';
}
The issue is however that the values in the foreach loop order are pre-defined and can't be changed but I need to output them in a different order.
Is it possible to re-arrange them into a custom order (That doesn't follow any standard direction, alphabetical, numerical, etc.)
Take a look at some of these http://php.net/manual/en/array.sorting.php,
CUSTOM ordering like, having apple, pear, orange, and wanting them in orange, apple, pear order isn't possible without literally adding them in yourself.
You COULD say...
if($component == "thisOne"){
add to array
}elseif($component == "thisOtherOne"{
add to array
}elseif....
From ranking your highest to lowest but that's very inefficient..
As you need a custom sorting algorithm, you can use usort function. From what I can see in the question, you don't care about indices, therefore you can do something like this:
$components = usort(
array_values($config['components']),
function ($a, $b)
{
//provide your custom sort function here
//$a and $b are two "components"
//for any given pair of $a and $b
//return -1 if $a should go before $b,
//return 0 if they are the same
//return 1 if $a should go after $b
}
);
foreach ($components as $component) {
$echo "<li>{$component['name']}</li>";
}
Have a look at usort function documentation for more information.
Can't seem to get my head around of sorting laravel collection so empty / null data would end up being last. ( bit confused about usort )
Pretty much all I have is bunch of times / timestamps that need to be ordered. Some rows may not have for that column.
I would like data to appear ASC / ascending while empty/null data is shown last.
$collection->sortBy('timestamp') sorts nicely but doesn't know how to deal with empty fields.
Table looks like this.
$data = $data->sort(function($a, $b) use ($sortBy) {
if ($a->{$sortBy} and $b->{$sortBy}) return 0;
return ($a->{$sortBy} > $b->{$sortBy}) ? -1 : 1;
});
Random code I tried from the internet, which I can't get to work correctly.
$sortBy contains a field name to sort by ( since it may change )
Faulty code deals with empty / null data but its out of order.
Have to use sort() with a closure. Below will sort timestamp ASC with NULL at the end.
$sorted = $collection->sort(function ($a, $b) {
if (!$a->timestamp) {
return !$b->timestamp ? 0 : 1;
}
if (!$b->timestamp) {
return -1;
}
if ($a->timestamp == $b->timestamp) {
return 0;
}
return $a->timestamp < $b->timestamp ? -1 : 1;
});
Try:
$collection->sortBy('-timestamp')
Does it work?
I had a similar issue. In my case, the time attribute of a $result might be NULL. It acts as if NULL is 0 (as int) and that's expected behavior. But I also wanted to sort the collection by leaving NULL last.
$collection->sortBy(function ($result) {
if ($result['time'] === NULL) {
return PHP_INT_MAX;
}
return $result['time'];
});
You can achieve this simply by returning an value higher in the alphabetical order compared to all other values in the array. i.e. PHP_INT_MAX to be safe. This will make sure all the results where the time equals NULL are at the end of the array.
Similar to Mr. Speelman's solution, but as a shorter PHP 7.4+ version:
$collection->sortBy(fn($e) => $e->timestamp ?: PHP_INT_MAX)
I assume your timestamp is unix timestamp.
You can sort it like this :
$sorted = $collection->sortByDesc('timestamp');
For several days now I am trying to cope with the algorithm implementation at the online shop which I am writing in PHP. I do not know whether the problem is only the implementation, or perhaps bad algorithm design. Hovewer, for me it seems fine. I only haven`t checked its complexity of it, but it's such a problem.
After a long deliberation on the same algorithm, without thinking on implementation I came up with the use of binary search tree (bst) with additional data inserted into list consist of user defined info (later about it). The whole orders list would be displayed, or returned using inorder method.
I write it like that:
If the input object date is greater than current object go right
If the input object date is less than current object go left
If the dates are the same stay at place
If the field is blank check if the product is in stock
If it is put into place and finish
If there is not do nothing and exit
If the field is full
{Check if on the list is this user id
If yes than check order priority
If no do nothing and exit
Check if there is product on stock
If yes replace record and exit
If no do nothing and exit
}
{If there is not user id on the list check if product is on stock
If yes then put element on the end
If no do nothing and exit
}
Maybe it looks a little bad, but I was not able to do indentation.
Data is transferred into algorithm in a loop until the end of orders list. The list is unordered.
This is my implementation:
class BinaryTree {
private $predescor = array(
'd'=>array('data'=>0),
'p'=>null,
'r'=>null,
'l'=>null
);
private $ancestor = array(
'd'=>array('data'=>0),
'p'=>null,
'r'=>null,
'l'=>null
);
private $i = 0;
public function insertIntoTree(&$root,$element)
{
$this->predescor = $root;
$this->predescor;
while($this->predescor)
{
if($element['d']['data']==$this->predescor['d']['data'])
{
$this->inertIntoList($element,$this->predescor['d']);
return true;
}
$this->predescor = $this->predescor;
if($element['d']['data']<$this->predescor['d']['data'])
{
$this->predescor = $this->predescor['l'];
}
else
{
$this->predescor = $this->predescor['r'];
}
}
$element['p'] = $this->predescor;
if(!$this->predescor)
{
$root = $element;
}
else if($element['d']['data']<$this->predescor['d']['data'])
{
$this->predescor['l'] = $element;
}
else
{
$this->predescor['r'] = $element;
}
return true;
}
public function putList(&$list,$root)
{
if($root!=null)
{
$this->putList($list, $root['l']);
$lista[$this->i] = $root;
$this->i++;
$this->putList($list, $root['r']);
}
return;
}
private function insertIntoList($element,&$position)
{
if($position == null)
{
$position = $element;
return true;
}
foreach($position['user'] as &$key)
{
if($key == $element['d']['user'])
{
if($key['priority']<$element['d']['user']['priority'])
{
return false;
}
else if($key['priority']==$element['d']['user']['priority'])
{
return false;
}
else
{
if(Orders::checkOrder($element['d']['user']['order']))
{
$key['order'] = $element['d']['user']['order'];
return true;
}
else
{
return false;
}
}
}
}
//#todo add at the end
return true;
}
}
I would like to advise whether there is a simpler way than using bst consisting of a quite complex arrays, which would also be easier to implement? Because now I can not inplement it in PHP.
Thank you in advance.
I wouldn't start by coding this in php at all.
I'd start by building this into the database. ("Orders" implies a database.) I'd start by clarifying a couple of points. Assuming that one order can have many line items . . .
The number of days since the last order seems to clearly apply to the order,
not to individual products.
The user can have "only one request carried at a time". Request for
what? Doesn't seem to make sense for this to apply either to an
order or to an order's line item.
The order priority seems to clearly apply to the order, not to line
items. But a line-item priority might make more sense. (What products does the customer need first?)
Whether the product is in stock seems to apply to the line items, not
to the order as a whole.
I'd start by creating two views. (Not because you'll eventually need two views, but because some things are still unclear.)
One view, which has to do with "ranking" as applied to an order, would calculate or display three things.
Number of days since the last order.
Is this order the "one request carried at a time"?
The order priority.
If the numbers assigned to these three things are consistent in scale, you can just sort on those three columns. But that's not likely. You'll probably need to weight each factor, possibly by multiplying by a "weighting" factor. A calculation on the result should let you put these in a useful order. It's not yet clear whether the calculation is best done in the view or in a stored procedure.
The other view would have to do with whether a line item is in stock. It's not clear whether one line item out of stock means the whole order is incomplete, or whether one line item out of stock changes the calculation of a weighted number that scales along with the others above. (You can make a good argument for each of those approaches.)
I want to get all of the SiteTree pages of a Silverstripe website and then sort them by depth descending. By depth I mean the number of parents they have.
This is already done to some extent by the Google Sitemaps Module. Except it doesn't go past a depth of 10 and doesn't calculate for pages hidden from search: https://github.com/silverstripe-labs/silverstripe-googlesitemaps
Looking at the Google Sitemaps Module module it appears easy enough to count the number of parents of a page: ( /code/GoogleSitemapDecorator.php - line 78)
$parentStack = $this->owner->parentStack();
$numParents = is_array($parentStack) ? count($parentStack) - 1: 0;
But what is the best way to sort the SiteTree using this calculation?
I'm hoping there's an easier way than getting all of the SiteTree, appending the depth, and then resorting.
The function below will get all published pages from the database, checks they are viewable and not an error page, finds out how many ancestors they have and then returns a DataObjectSet of the pages ordered by depth (number of ancestors).
public function CustomPages() {
$filter = '';
$pages = Versioned::get_by_stage('SiteTree', 'Live', $filter);
$custom_pages = new DataObjectSet();
if($pages) {
foreach($pages as $page) {
if( $page->canView() && !($page instanceof ErrorPage) ) {
$parentStack = $page->parentStack();
$numParents = is_array($parentStack) ? count($parentStack) - 1 : 0;
$page->Depth = $numParents;
$custom_pages->push($page);
}
}
}
$custom_pages->sort('Depth', 'DESC');
return $custom_pages;
}
If you're using PostgreSQL you can also use a single database query. In our effort to speed up SilverStripe (2.4), we've replaced the getSiteTreeFor() with a single database query, see:
http://fvue.nl/wiki/SilverStripe:_Replace_getSiteTreeFor_with_single_query
The breadcrumb field contains an array of ID's on which you can sort.