Sorting PHP arrays holding additional arrays - php

I have an array in PHP which has associative index values as show below. They are irrelevant to the sorting that I need to do, but I need to keep them.
Each value in this array is actually another array which might have 1 to N integers in it, sorted in descending order. However, the integers are written down as text (strings). So the initial data looks something like that:
[10025] => 51, 51, 43, 43, 30, 29, 28, 27, 25, 24, 22, 21
[15671] => 24, 21
[02672] => 24, 26
[76935] => 87, 72, 69, 67, 55, 43, 43, 40, 35, 33, 29
I need to sort this by looking at the maximum value of each secondary array. If the values are equal, I have to look at the second value, etc so finally I'll get something like this:
[76935] => 87, 72, 69, 67, 55, 43, 43, 40, 35, 33, 29
[10025] => 51, 51, 43, 43, 30, 29, 28, 27, 25, 24, 22, 21
[02672] => 24, 26
[15671] => 24, 21
arsort() by default looks at the number of items in each array, which doesn't do the job and raising the SORT_NUMERIC doesn't work either - can't figure out what it does exactly but it doesn't sort the array the way I want.
I've also looked at array_multisort(), however, playing around with it, I couldn't get the desired result either.

Try this, take note of the uasort() function (that's where the magic happens):
<?php
$data = array(
10025 => '51, 51, 43, 43, 30, 29, 28, 27, 25, 24',
15671 => '24, 21',
02672 => '24, 26',
76935 => '87, 72, 69, 67, 55, 43, 43, 40, 35, 33, 29'
); //Inital data array
echo '<pre>'.print_r($data, true).'</pre>'; //Print the array as it is
uasort($data, function($a, $b) { //Sort the array using a user defined function maintaining array indices
$aArray = explode(', ', $a); //Split the strings into proper arrays
$bArray = explode(', ', $b);
rsort($aArray); //Sort the proper arrays from largest to smallest
rsort($bArray);
return $aArray[0] > $bArray[0] ? -1 : 1; //Compare the first element in each array (because it is the largest thanks to the rsort functions)
});
echo '<pre>'.print_r($data, true).'</pre>'; //Print the array in it's new order
?>
Output:
Array
(
[10025] => 51, 51, 43, 43, 30, 29, 28, 27, 25, 24
[15671] => 24, 21
[1466] => 24, 26
[76935] => 87, 72, 69, 67, 55, 43, 43, 40, 35, 33, 29
)
Array
(
[76935] => 87, 72, 69, 67, 55, 43, 43, 40, 35, 33, 29
[10025] => 51, 51, 43, 43, 30, 29, 28, 27, 25, 24
[1466] => 24, 26
[15671] => 24, 21
)

array_multisort() should do the trick:
$yourdata; // your assosiative array with data
$lengths = array();
foreach ($yourdata as $key => $row)
{
$lengths[$key] = $row.length;
}
array_multisort($lengths, SORT_DESC, $yourdata);
Reference url http://www.php.net/manual/ro/function.array-multisort.php

uasort(
$myArray,
function($a, $b) {
$a = explode(',',$a);
$b = explode(',',$a);
$i = 0;
while (true) {
if (isset($a[$i]) && isset($b[$i]) && $a[$i] !== $b[0]) {
return ($a[$i] < $b[$i]) ? -1 : 1;
} elseif (isset($a[$i]) && !isset($b[$i])) {
return 1;
} elseif (isset($b[$i]) && !isset($a[$i])) {
return -1;
} elseif (!isset($b[$i]) && !isset($a[$i])) {
return 0;
}
$i++;
}
}
);

Related

Creating a list and indexing

Given a two list. Create a third list by picking an odd-index element from the first list and even index elements from the second.
For Example:
listOne = [3, 6, 9, 12, 15, 18, 21]
listTwo = [4, 8, 12, 16, 20, 24, 28]
Expected Output:
Printing Final third list
[6, 12, 18, 4, 12, 20, 28]
I think, it will be helpful for you.
<?php
$listOne = [3, 6, 9, 12, 15, 18, 21];
$listTwo = [4, 8, 12, 16, 20, 24, 28];
$NlistOne=[];
$NlistTwo=[];
//odd-index positions from list one [6, 12, 18]
foreach ($listOne as $key => $value) {
if($key%2==1){
array_push($NlistOne, $value);
}
}
//even-index positions from list two [4, 12, 20, 28]
foreach ($listTwo as $key => $value) {
if($key%2==0){
array_push($NlistTwo, $value);
}
}
//Printing Final third list [6, 12, 18, 4, 12, 20, 28]
print_r(array_merge($NlistOne,$NlistTwo));
?>
Output will be:
Array ( [0] => 6 [1] => 12 [2] => 18 [3] => 4 [4] => 12 [5] => 20 [6] => 28 )
//init result array
//loop over listOne, using for($i=1;$i<sizeof($listOne);$i=$i+2)
//and add to result for each iteration, $resultArr[] = $listOne[$i]
//do the same with listTwo, but for($i=*0*
You can merge both of arrays and then pick all odd elements
$listOne = [3, 6, 9, 12, 15, 18, 21];
$listTwo = [4, 8, 12, 16, 20, 24, 28];
$result = [];
foreach ( array_merge($listOne, $listTwo) as $value ){
if ( $key % 2 ) {
$result[] = $value;
}
}
If array length isn't fixed, say it could contain not 7 elements, then you need to check it before merging to make it have odd number of elements
$listOne = [3, 6, 9, 12, 15, 18, 21, 777];
$listTwo = [4, 8, 12, 16, 20, 24, 28];
$result = [];
if ( count($listOne) % 2 !== 1 ) {
$listOne[] = 0;
}
foreach( array_merge($listOne, $listTwo) as $value ){
if ( $key % 2 ) {
$result[] = $value;
}
}
you don't have to loop over whole array array_filter will do this for you, Constant ARRAY_FILTER_USE_KEY will check each key for odd or for even
<?php
$a1 = [3, 6, 9, 12, 15, 18, 21];
$a2 = [4, 8, 12, 16, 20, 24, 28];
function odd($var)
{
// returns whether the input integer is odd
return $var & 1;
}
function even($var)
{
// returns whether the input integer is even
return !($var & 1);
}
$result= (array_merge(array_filter($a1, 'odd', ARRAY_FILTER_USE_KEY),array_filter($a2, 'even', ARRAY_FILTER_USE_KEY)));
output you will get
Array (
[0] => 6
[1] => 12
[2] => 18
[3] => 4
[4] => 12
[5] => 20
[6] => 28 )
Iterate over first one and take only values of odds indices, and loop again through second one and take evens indices.
$listOne = [3, 6, 9, 12, 15, 18, 21];
$listTwo = [4, 8, 12, 16, 20, 24, 28];
$res = [];
for ($i=0; $i < count($listOne); $i++) {
if($i & 1) // $i is odd.
$res[] = $listOne[$i];
}
for ($j=0; $j < count($listTwo); $j++) {
if(n % 2 === 0) // $j is even.
$res[] = $listTwo[$j];
}
Result:
echo "List One :".json_encode($listOne)."<br>";
echo "List Two :".json_encode($listTwo)."<br>";
echo "Lists Merged:".json_encode($res);
Output:*
/*
List One :[3,6,9,12,15,18,21]
List Two :[4,8,12,16,20,24,28]
Lists Merged:[6,12,18,4,12,20,28]
*/
Iterate over arrays:
take odds in array starting with index One and increment it by two.
take evens in array by starting with index Zero and increment it by two.
$listOne = [3, 6, 9, 12, 15, 18, 21]; $listTwo = [4, 8, 12, 16, 20, 24, 28];
$res = [];
for ($i=1; $i < count($listOne); $i+=2) {
$res[] = $listOne[$i];
}
for ($j=0; $j < count($listTwo); $j+=2) {
$res[] = $listTwo[$j];
}
print(json_encode($res)); // [6,12,18,4,12,20,28]

For-each loop, larger than 20 into another array

I'm stuck on this exercise that I can't seem to get for the life of me.
$numbers2 = [21, 5, 4, 6, 76, 2, 18, 85, 55, 1];
foreach ($numbers2 as &$value) {
$largeNumbers[] = $value > 20;
}
That's the code I have so far. What I am trying to do here is use a for-each loop to add all the numbers that are larger than 20 into another Array, which I have named $largeNumbers. The code I have inserted above seems to be printing out true and false values, which is not what I was going for. I'd really appreciate it someone could tell me what I'm possibly doing wrong or even show me a better way. I have to use a for-each loop.
For each item, you are checking if it's larger than 20, which results in a boolean value (it either is or is not), and you then store this value to the result array. Instead, you could use an if statement` to only take the elements that answer the condition:
foreach ($numbers2 as &$value) {
if ($value > 20) {
$largeNumbers[] = $value;
}
}
<?php
$nums = [21, 5, 4, 6, 76, 2, 18, 85, 55, 1];
$less_than_or_equal_to_20 = [];
foreach($nums as $v)
if($v <= 20)
$less_than_or_equal_to_20[] = $v;
$out = array_diff($nums, $less_than_or_equal_to_20);
var_export($out);
Output:
array ( 0 => 21, 4 => 76, 7 => 85, 8 => 55, )
<?php
$nums = [21, 5, 4, 6, 76, 2, 18, 85, 55, 1];
foreach([$nums] as $v)
$out = array_filter($v, function($v) {
return $v > 20;
});
var_export($out);
Output:
array ( 0 => 21, 4 => 76, 7 => 85, 8 => 55, )

Find peaks in a series?

I have got a series like this:
14, 13, 12, 14, 15, 18, 20, 17, 15, 19, 22, 24, 22, 18, 15, 14, 17, ...
If I plot these points on a chart on X-Y axis using these values as Y coordinates, then you'll see that there are peaks at 20 & 24.
I want to find all these peaks in the series
I tried:
$a=array(14, 13, 12, 14, 15, 18, 20, 17, 15, 19, 22, 24, 22, 18, 15, 14, 17 );
rsort($a);
echo $a[0];
echo $a[1];
But that doesn't give me the two peaks I see on the graph. The result of the code above is 24, and 22. But the peak on the graph were made by 20 and 24...
Is there a way I can detect the array to determine the peaks in the entire series?
I don't need a working code. Just some ideas I can work upon.
As Mark Baker suggested, you need to check that each value in the array is greater than the previous and the next value. That's what defines a peak.
Just be sure to start from the 2nd index and finish on the nth-1 item or else you'll get an Undefined offset error.
$a = array(14, 13, 12, 14, 15, 18, 20, 17, 15, 19, 22, 24, 22, 18, 15, 14, 17);
$arr = [];
for($i=1; $i<count($a)-1; $i++){
if($a[$i] > $a[$i+1] && $a[$i] > $a[$i-1]) {
array_push($arr, $a[$i]);
}
}
var_dump($arr); //returns: array(2) { [0]=> int(20) [1]=> int(24) }
Depending on your application, you might want to add some logic in case the case that 2 or more items "together" are a peak. For example, 22, 24, 24, 22, 18. If this is something you want, just change the logic to check for >= on the next item:
if($a[$i] > $a[$i+1] && $a[$i] >= $a[$i-1]) {
This will yield the same result as above.
<?php
$a=array(14, 13, 12, 14, 15, 18, 20, 17, 15, 19, 22, 24, 22, 18, 15, 14, 17 );
$last=0;
$peaks=array();
$upwards=false;
foreach( $a as $value )
{
if( $value > $last )
{
$upwards = true;
}
if( $value < $last )
{
if( $upwards )
{
$peaks[] = $last;
}
$upwards = false;
}
$last = $value;
}
var_dump($peaks);
Old question, guess it popped up because someone was editing, but here's my two cents' worth. This is better done without a loop:
<?php
$a = [14, 13, 12, 14, 15, 18, 20, 17, 15, 19, 22, 24, 22, 18, 15, 14, 17];
$b = array_filter(
$a,
function($v, $k) use($a) {return $k > 0 && $v > $a[$k-1] && $k + 1 < count($a) && $v > $a[$k+1];},
ARRAY_FILTER_USE_BOTH
);
print_r($b);
Result:
Array
(
[6] => 20
[11] => 24
)

How To Perform array_search In Multi Dimensional Array?

I have this code :
$order_list = array ( array ("tangible", 1, 8, 33, 19000),
array ("tangible", 1, 9, 8, 19000),
array ("tangible", 6, 3, 24, 19000),
array ("tangible", 6, 2, 10, NULL),
array ("tangible", 1, 17, 11, 28000));
$key = array_search(3, array_column($order_list, $order_list[2]));
and I want to get the value of $order_list[$i][3] based on $order_list[$i][2].
for example, if I put :
3 I will get 24 in return
9 I will get 8 in return
and so on...
I tried to use array_search :
$key = array_search(3, array_column($order_list, $order_list[2]));
but I got this error :
Warning: array_column(): The column key should be either a string or an integer in /home/***/public_html/***.php on line 8
Warning: array_search() expects parameter 2 to be array, boolean given in /home/***/public_html/***.php on line 8
how to perform array_serach in this case? thanks before.
I created a general purpose function to get next value of current value in 2'd array. have a look on below function. Also look at variable description of function to understand input in function:
/***
* #param array $array input array
* #param $search_value value that need to be searched
* #param $search_index index of inner array where current value exists
* #return next value of current value
*/
function getNextSequence(array $array, $search_value, $search_index)
{
$result = null;
$key = array_search($search_value, array_column($array, $search_index));
if ($key !== false) {
$result = (isset($array[$key][$search_index + 1])) ? $array[$key][$search_index + 1] : null;
}
return $result;
}
$order_list = array(
array("tangible", 1, 8, 33, 19000),
array("tangible", 1, 9, 8, 19000),
array("tangible", 6, 3, 24, 19000),
array("tangible", 6, 2, 10, NULL),
array("tangible", 1, 17, 11, 28000)
);
var_dump(getNextSequence($order_list, 3, 2)); //output: int(24)
var_dump(getNextSequence($order_list, 9, 2)); //output: int(8)
var_dump(getNextSequence($order_list, 10, 2)); //output: Null
var_dump(getNextSequence($order_list, 2, 2)); //output: int(10)
<?php
$search = 9;
$order_list = array ( array ("tangible", 1, 8, 33, 19000),
array ("tangible", 1, 9, 8, 19000),
array ("tangible", 6, 3, 24, 19000),
array ("tangible", 6, 2, 10, NULL),
array ("tangible", 1, 17, 11, 28000));
foreach ($order_list as $string){
if (in_array($search,$string)){
//repsonse here
}
}
?>
another way.....
$search = 9;
$order_list = array ( array ("tangible", 1, 8, 33, 19000),
array ("tangible", 1, 9, 8, 19000),
array ("tangible", 6, 3, 24, 19000),
array ("tangible", 6, 2, 10, NULL),
array ("tangible", 1, 17, 11, 28000));
foreach ($order_list as $string){
if ($string[2] == $search){
print_r( $string);
}
}

Create rows of elements based on an array of length values

Is there a neat way to split an array into chunks based on an array of lengths?
Input:
$start = range(0, 30);
$length = [3, 7, 2, 12, 6];
Desired Output:
[
[0, 1, 2], // 3
[4, 5, 6, 7, 8, 9, 10], // 7
[11, 12], // 2
[13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24], // 12
[25, 26, 27, 28, 29, 30], // 6
];
Using array_splice:
$target = array(); // or use [] in PHP 5.4
foreach($length as $i) {
$target[] = array_splice($start, 0, $i);
}
Try it.
Be advised, this changes $start!
This is very easily accomplished with the following:
Code:
$target = array();
$offset = 0;
foreach ($length as $lengthValue) {
$target[] = array_slice($start, $offset, $lengthValue);
$offset += $lengthValue;
}
var_dump($target);
Explanation:
What you are doing here is using the array_slice() method (very similar to the substr) to extract the portion of the array and then inserting it into the target array. Incrementing the offset each time allows the function to remember which one to use next time.

Categories