I have a list of ids [1,500,7] and a list of entities with corresponding id properties, but in a different order (1 -> 7, 500). In php, how can I sort them according to the list of ids?
Php version is 5.3.2
Given some data structures like the following (from your description):
$ids = array(5, 15, 10);
$values = array(
(object) array('id' => 10, 'data' => 'foo'),
(object) array('id' => 5, 'data' => 'foo'),
(object) array('id' => 15, 'data' => 'foo'),
);
You could use something like the following:
// Precalculate sort positions to avoid two calls to array_search for each sort
// operation as that gets costly very quickly.
$id_positions = array_flip($ids);
// Do the actual sorting here.
usort($values, function($a, $b) use ($id_positions) {
return ($id_positions[$a->id] < $id_positions[$b->id] ? -1 : 1);
});
The above code makes a few assumptions, but should get you on your way.
You can create a compare function (or method inside a class) that should return a positive, negative or 0 value depending if the first value to compare is greater, lesser or equal to the second value. And then call usort() (in this link you'll find more info about the compare function).
Assuming that the list of IDs and the list of entities are the same length and that every ID has a corresponding entity and that every entity's ID is in the ID list.
$sorted_entities = array();
$sort_order = array_combine($list_of_ids, range(0, count($list_of_ids) - 1));
foreach ($list_of_entities as $entity) {
$sorted_entities[$sort_order[$entity->id]] = $entity;
}
This has the advantage of traversing each array only once. It has the disadvantage that it does not sort the entities in place.
Related
I have an array and want to fill the some items depend on previous item's values:
$order = array(
'items_price' => 200,
'tax_price' => 18,
'total_price' => function () {
return $order.items_price + $order.tax_price;
})
How can I do that?
Well, that actually is doable. You cannot reference values in the array from within the array itself; but if you declare variable references for the needed array values, you can pass those references to the closure (function) using use :
$order = array(
'items_price' => ($ip = 200),
'tax_price' => ($tp = 18),
'total_price' => function() use ($ip, $tp) {
return $ip + $tp;
}
);
echo $order['total_price']();
That echoes out 218 ..! But the downside is that the passed references / params are static. Try
$order['items_price'] = 100;
echo $order['total_price']();
The result are a disappointing 218 ... Even though items_price actually are changed to 100, the params passed to total_price are the same as when the array were created. They are not magically updated, the array are not a piece of dynamic code, it is a data structure.
It was a very interesting question indeed, but I do not see any practically use. It seems to me you in this case should use classes instead of arrays.
I have an algorithm that users can manually enter and it will display the header and footer. Although in my function there is a priority field, and I want everything to be sorted with priority.
3 Parameters - Field, Priority, Type
Keep in mind I already have a array of head/foot with priority preset, this function is to add additional or overwrite the priority. It then gets stored in another array to simplify. I then want to sort it all with the priority.
Array looks like this:
'js-amcharts-export' => array('link' => 'assets/global/plugins/amcharts/amcharts/plugins/export/export.min.js',
'type' => 'text/javascript',
'default' => false,
'priority' => ''),
Example:
$script->require_meta('js-amcharts-export', '20')
This would make sure all the other ones with priority 10 lets say get loaded first then the 20, and everything after.
Basically I need to sort the array based on 'priority'
Would asort($header_array['priority'] work?
I think the PHP array sort you are looking for uasort.
http://php.net/manual/en/function.uasort.php
+1 to #user3720435 for the answer.
In your case, the code would look like this:
function cmp($a, $b) {
if ($a['priority'] == $b['priority']) {
return 0;
}
return ($a['priority'] < $b['priority']) ? -1 : 1;
}
uasort($header_array, 'cmp');
I have a function which returns an array:
This is the fucntion:
{
global $woocommerce;
$items = $woocommerce->cart->get_cart();
$product_names=array();
foreach($items as $item => $values) {
$_product = $values['data']->post;
$product_names[]=$_product->post_title;
}
$allproductname=print_r($product_names, true);
return $allproductname;
}
The output of this function:
Array(
[0] => Social media campagne
[1] => Marketingcampagne
)
I also have 2 arrays named group1 and group2.
$group1=array("Social media campagne","Marketingcampagne");
$group2=array("SEO","Facebook marketing");
Now what i want to check is if both of the values of the function output belong in $group1 then i want it to print "both values belong in group1"
And if 1 value belongs in group1 and one value in group 2 then echo "1 value belongs in group1 and one value belongs in group2"
--edit
I forgot to mention that this output can change its not always this:
Array(
[0] => Social media campagne
[1] => Marketingcampagne
)
it can also be 3 products for example
It looks like what you want to do relates to set theory. You have a set of things (the return from the function) and you want to see if all the elements in that array are in another array (You want to check if the return array is a strict subset of the array you're checking against).
You can use array_intersect () to do this. This function takes at least 2 arrays as its arguments, and it returns an array of elements that exist in all the arrays passed to it.
If you have an array specifying all the values you want to check against and another array that may or may not be a subset of that array then you can use array_intersect to get a list of all the elements in both arrays. If the number of elements in the output match the number of elements in the array that you want to check then the array must be a strict subset of the array you're checking against.
The following code demonstrates the basic principle:
$set = [1, 2, 3, 4, 5, 6, 7, 8]; // Data you want to test against
$subset = [2, 4, 6, 8]; // Strict subset of $set
$notSubset = [2, 4, 6, 10]; // Not a strict subset of $set because it contains 10
var_dump (count ($subset) === count (array_intersect ($set, $subset))); // true
var_dump (count ($notSubset) === count (array_intersect ($set, $notSubset))); // false
NOTE: array_intersect is only really suitable when the contents of the arrays being compared are all primitive types, as PHP will cast them all to string while comparing them. If you're comparing arrays of arrays or arrays of object then you're better off using array_uintersect () and specifying how the elements are compared yourself with a callback function.
To apply the array_intersect() from #GordonM to your problem (it seems like there are only strings in the arrays).
You first get the number of values from your output, that are in group1
$inGroup1 = array_intersect($ouput, $group1);
$inGroup1Count = count($inGroup1); // you can rewrite these two lines into one,
// if the actual matches are of no interest
If this is the same count as $output, than it's group1. If not, check how many are in group2. I guess you can code that yourself. And then check if inGroup1Count and inGroup2Count are both > 0. Than it's in both groups.
For logic reason you should also check if all are in group 2. Or is that impossible?
With checking both groups you can create an output like
echo $inGroup1Count.' value(s) belongs in group1 and '.$inGroup2Count.' value(s) belongs in group2';
There are numerous questions here asking how to sort a multi-dimensional array in PHP. The answer is usort(). I know that. But I have a question that takes it a bit further, and I couldn't see a similar answer here.
I have an array of records, each of which includes a country ID (or a country name if you prefer; it's not relevant).
My task is to sort the array in such a way as to favour certain countries. This is dynamic -- ie the choice of countries to favour is determined by the user's config. I have a separate array which specifies the required sort order for the first few countries; results from other countries would just be left unsorted at the end of the list.
So the question is: how do I get the this sort criteria into usort() without resorting to using a global variable. And preferably without injecting the criteria array into every element of the main array ('coz if I'm going to loop it anyway, what's the point in using usort() at all?)
Please note: Since it's going to be relevant to the answers here, I'm stuck on PHP 5.2 for the time being, so I can't use an anonymous function. We are in the process of upgrading, but for now I need answers that will work for 5.2. (answers for 5.3/5.4 will be welcome too, especially if they make it significantly easier, but I won't be able to use them)
You explicitly write that you do not want to have global variables, so I do not make you a suggestion with static variables as well because those are actually global variables - and those are not needed at all.
In PHP 5.2 (and earlier) if you need call context within the callback, you can create your context by making use of a class of it's own that carries it:
class CallContext
{
}
For example you have the compare function for sort:
class CallContext
{
...
public function compare($a, $b)
{
return $this->weight($a) - $this->weight($b);
}
public function getCallback()
{
return array($this, 'compare');
}
...
}
That function can be used as the following as a callback with usort then:
$context = new CallContext();
usort($array, $context->getCallback());
Pretty straight forward. The private implementation of CallContext::weight is still missing, and from your question we know it needs some sort data and information. For example the name of the key of the country id in each record. Lets assume records are Stdclass objects so to get the weight of one record the context class needs to know the name of the property, the sort-order you define your own and a sort-value for those country-ids that are not defined in the custom sort order (the others, the rest).
These configuration values are given with the constructor function (ctor in short) and are stored as private members. The missing weight function then converts a record into the sort-value based on that information:
class CallContext
{
private $property, $sortOrder, $sortOther;
public function __construct($property, $sortOrder, $sortOther = 9999)
{
$this->property = $property;
$this->sortOrder = $sortOrder;
$this->sortOther = $sortOther;
}
private function weight($object) {
if (!is_object($object)) {
throw new InvalidArgumentException(sprintf('Not an object: %s.', print_r($object, 1)));
}
if (!isset($object->{$this->property})) {
throw new InvalidArgumentException(sprintf('Property "%s" not found in object: %s.', $this->property, print_r($object, 1)));
}
$value = $object->{$this->property};
return isset($this->sortOrder[$value])
? $this->sortOrder[$value]
: $this->sortOther;
}
...
The usage now extends to the following:
$property = 'country';
$order = array(
# country ID => sort key (lower is first)
46 => 1,
45 => 2
);
$context = new CallContext('country', $order);
usort($array, $context->getCallback());
With the same principle you can very often convert any PHP 5.3 closure with use clauses to PHP 5.2. The variables from the use clause become private members injected with construction.
This variant does not only prevent the usage of static, it also makes visible that you have got some mapping per each element and as both elements are treated equal, it makes use of a private implementation of some weight function which works very well with usort.
I hope this is helpful.
You might not want a global variable, but you need something that behaves like one. You could use a class with static methods and parameters. It won't pollute the global scope that much and it would still function the way you need it.
class CountryCompare {
public static $country_priorities;
public static function compare( $a, $b ) {
// Some custom sorting criteria
// Work with self::country_priorities
}
public static function sort( $countries ) {
return usort( $countries, array( 'CountryCompare', 'compare' ) );
}
}
Then just call it like this:
CountryCompare::country_priorities = loadFromConfig();
CountryCompare::sort( $countries );
You can use closures (PHP >= 5.3):
$weights = array( ... );
usort($records, function($a, $b) use ($weights) {
// use $weights in here as usual and perform your sort logic
});
See Demo : http://codepad.org/vDI2k4n6
$arrayMonths = array(
'jan' => array(1, 8, 5,4),
'feb' => array(10,12,15,11),
'mar' => array(12, 7, 4, 3),
'apr' => array(10,16,7,17),
);
$position = array("Foo1","Foo2","Foo3","FooN");
$set = array();
foreach($arrayMonths as $key => $value)
{
$max = max($value);
$pos = array_search($max, $value);
$set[$key][$position[$pos]] = $max ;
}
function cmp($a, $b)
{
foreach($a as $key => $value )
{
foreach ($b as $bKey => $bValue)
{
return $bValue - $value ;
}
}
}
uasort($set,"cmp");
var_dump($set);
output
array
'apr' =>
array
'FooN' => int 17
'feb' =>
array
'Foo3' => int 15
'mar' =>
array
'Foo1' => int 12
'jan' =>
array
'Foo2' => int 8
another example:-
Sorting a Multi-Dimensional Array with PHP
http://www.firsttube.com/read/sorting-a-multi-dimensional-array-with-php/
Every so often I find myself with a multidimensional array that I want to sort by a value in a sub-array. I have an array that might look like this:
//an array of some songs I like
$songs = array(
'1' => array('artist'=>'The Smashing Pumpkins', 'songname'=>'Soma'),
'2' => array('artist'=>'The Decemberists', 'songname'=>'The Island'),
'3' => array('artist'=>'Fleetwood Mac', 'songname' =>'Second-hand News')
);
The problem is thus: I’d like to echo out the songs I like in the format “Songname (Artist),” and I’d like to do it alphabetically by artist. PHP provides many functions for sorting arrays, but none will work here. ksort() will allow me to sort by key, but the keys in the $songs array are irrelevant. asort() allows me to sort and preserves keys, but it will sort $songs by the value of each element, which is also useless, since the value of each is “array()”. usort() is another possible candidate and can do multi-dimensional sorting, but it involves building a callback function and is often pretty long-winded. Even the examples in the PHP docs references specific keys.
So I developed a quick function to sort by the value of a key in a sub-array. Please note this version does a case-insensitive sort. See subval_sort() below.
function subval_sort($a,$subkey) {
foreach($a as $k=>$v) {
$b[$k] = strtolower($v[$subkey]);
}
asort($b);
foreach($b as $key=>$val) {
$c[] = $a[$key];
}
return $c;
}
To use it on the above, I would simply type:
$songs = subval_sort($songs,'artist');
print_r($songs);
This is what you should expect see:
Array
(
[0] => Array
(
[artist] => Fleetwood Mac
[song] => Second-hand News
)
[1] => Array
(
[artist] => The Decemberists
[song] => The Island
)
[2] => Array
(
[artist] => The Smashing Pumpkins
[song] => Cherub Rock
)
)
The songs, sorted by artist.
The answer to your question is indeed in the usort() function. However, what you need to do is write the function that you pass to it is doing the weighting for you properly.
Most of the time, you have something like
if($a>$b)
{
return $a;
}
But what you need to do is something along the lines of
if($a>$b || $someCountryID != 36)
{
return $a;
}
else
{
return $b;
}
You need to use ksort to sort by weight, not usort. That will be much cleaner.
Arrange your data in an associative array $weighted_data in the format weight => country_data_struct. This is a very intuitive form of presentation for weighted data. Then run
krsort($weighted_data)
I'm new to the stackoverflow so feel free to delete this question if it's stupid:
The main purpose of doing this is to display a list of details on a page randomly everytime it refreshes, but the details are in different arrays, so i have 2 arrays which i have to randomize in the same way,
Example:
$Name[0]=John;
$Name[1]=Lucy;
$Name[2]=Mike;
$Age[0]=18;
$Age[1]=20;
$Age[2]=25;
after being randomize becomes :
$Name[2]=Mike;
$Name[0]=John;
$Name[1]=Lucy;
$Age[2]=25;
$Age[0]=18;
$Age[1]=20;
I tried using "->", For example:
$Array[0]->name = 'John';
$Array[0]->age = '18';
$Array[1]->name = 'Lucy';
$Array[1]->age = '20';
shuffle($Array);
but my teacher wasn't thrilled because the code was messy since there's alot of transfer (Details into the new array before the randomizing and new array back into details after randomizing). He wants me to do it only with the 2 arrays.
What you are doing, is sort of the right way of doing things. Look at it this way. What exactly are you trying to achieve? You want a set of data sorted randomly (ok, sorting is not the right word here, but you get it). Whenever you talk about any kind of reordering of data, we talk about changing the order that the elements appear in. Your element here is neither 'name', nor 'age'. Your element here is 'person'. So, your data list should be a list of 'person's, which you would reorder and not in two loosely-coupled arrays.
Anyways, if you absolutely MUST do it this way. This is what I suggest.
Let there be two arrays $A and $B. Let them both have N elements. What you do is, create an array $nums, which has elements from 0 to N-1 i.e.
$nums = array(0, 1, 2 ... N-1)
Now, shuffle the $sums array. So, let's say we have something like:
$num = (3, 4, 1, 2)
Ok, so we have created a mapping here,
$num[0] = 3
means that the 0th element is 3, which you can interpret as in the new array, the 0th element should be the 3rd element of the old array. To do this, run a simple loop:
for($i=0; $i<N; $i++) {
$A2[$i] = $A[$num[$i]];
$B2[$i] = $B[$num[$i]];
}
It is a slightly challenging task if you insist on this being an online solution (a solution that modifies the original data structure and not build a new one), but I'll leave that ask to you (think of swapping data elements according to the map we just created). Also, you could extend it easily to more than 2 arrays, or an array of arrays in case you're not sure how many arrays one has to deal with. Feel free to ask any question you might have...
You should use an associative array.
$people = array('John' => 18, 'Lucy' => 20, 'Mike' => 25);
return shuffle_assoc($people);
function shuffle_assoc( $array )
{
$keys = array_keys( $array );
shuffle( $keys );
return array_merge( array_flip( $keys ) , $array );
}