PHP order by value of array key - php

I have an array being created which contains a date:
$arr = array();
$arr["one"][] = array(
'due' => '17-01-2021 10:00:00',
);
$arr["one"][] = array(
'due' => '17-01-2021 09:00:00',
);
$arr["two"][] = array(
'due' => '19-01-2021 09:00:00',
);
$arr["two"][] = array(
'due' => '18-01-2021 09:00:00',
);
And I want to order by the value of the 'due' key
I tried adding this uasort function:
uasort($arr, function ($a, $b) {
return $a["due"] <=> $b["due"];
});
print_r($arr);
But that still shows in the order above, whereas it should be showing the '09:00:00' value first

Your approach wasn't really that far off, I basically just changed a few little things:
Loop over each "word-number-indexed" array individually (those indexed under one and two in the main array $arr).
Convert the due dates/times to unix timestamps to have an integer that can be compared. Without this, PHP tries to sort the strings on a character-by-character basis which does not work with your format but would work if your format was YYYY-MM-DD hh:mm:ss (because the "biggest" position value would be at the start).
Use usort instead of uasort since there is no point in keeping the keys (which are integers to begin with and neither one nor due in this context.
$arr = array();
$arr["one"][] = array(
'due' => '17-01-2021 10:00:00',
);
$arr["one"][] = array(
'due' => '17-01-2021 09:00:00',
);
$arr["two"][] = array(
'due' => '19-01-2021 09:00:00',
);
$arr["two"][] = array(
'due' => '18-01-2021 09:00:00',
);
foreach ($arr as &$numberIndex) {
usort($numberIndex, function ($a, $b) {
return strtotime($a["due"]) <=> strtotime($b["due"]);
});
}
print_r($arr);
Some side notes:
Note the & at &$numberIndex, without this PHP works on an in-place copy of your value and it is never changed in $arr.
This approach with strototime() only works if all your dates/times are after epoch time 0 (which is 1st of January 1970). If you need to use older dates you can create DateTime() objects within the sort callback.
The resulting array looks like this:
Array
(
[one] => Array
(
[0] => Array
(
[due] => 17-01-2021 09:00:00
)
[1] => Array
(
[due] => 17-01-2021 10:00:00
)
)
[two] => Array
(
[0] => Array
(
[due] => 18-01-2021 09:00:00
)
[1] => Array
(
[due] => 19-01-2021 09:00:00
)
)
)

Related

Search a multidimensional array for more than one specific value

I have a multidimensional array that looks something like this:
Array
(
[0] => Array
(
[day] => Every Sunday
[time] => 08:00
[form] => normal
)
[1] => Array
(
[day] => Every Monday
[time] => 10:30
[form] => special
)
[2] => Array
(
[day] => Every Wednesday
[time] => 17:00
[form] => normal
)
)
I'd like to check if it contains specific days. For example, I'd like to check if it contains Monday and Sunday.
Using array_search works fine for one day. E.g. if I only want to check for Sunday:
array_search('Sunday', array_column($times, 'day'));
But it returns nothing if I pass in more than one day and attempt to search for Sunday and Monday:
array_search(array('Sunday', 'Monday'), array_column($times, 'day'));
Is there another way of doing this, preferably without iterating through everything using a for loop?
Your array for times defines the key 'day' twice, so this array will not be valid.
try changing your input array to
$times = array(
'Monday',
'Sunday
);
Secondly, array_search() cannot accept an array as its first value, as it is expecting a search term please see:
http://php.net/manual/en/function.array-search.php
As array search returns the Key of the array of the found value, this is not what you need here, you could use:
http://php.net/manual/en/function.array-count-values.php
this will grab a unique list of values in your input array and return a key => value set with the value => count as a new array.
$times = array('Monday','Tuesday','Wednesday','Monday','Monday');
print_r(array_count_values($times));
should return:
array (
[Monday] => 3
[Tuesday] => 1
[Wednesday] => 1
)
UPDATE:
After op gave a multidimensional array, the solution would be
array_count_values(array_column($array, $key));
You can use;
print_r(array_count_values($times));
One issue is that the needle for array_search is explicit, unlike strpos. So Sunday will not match a value of Every Sunday, assuming that One Sunday or Every Sunday may be desired to be searched for.
Since you already know the key to search for, I suggest using array_filterwith strpos instead. Then you can simply use count($filteredTimes) to determine how many or use array_keys($filteredTimes) to retrieve the keys as it would array_search;
It will be the equivalent of using array_column and array_search or array_count_values or array_keys($array, $search).
But instead of only returning an array of keys (or single key) with 2 iterations, it will return the original array values that contain the desired criteria or be empty, with 1 iteration.
Example: https://3v4l.org/uCUMr
$filteredTimes = array_filter($times, function($v) {
return isset($v['day']) && (false !== strpos($v['day'], 'Sunday') || false !== strpos($v['day'], 'Monday'));
});
$matchingDays = count($filteredTimes);
var_export($filteredTimes);
var_dump($matchingDays);
Result
array (
0 =>
array (
'day' => 'Every Sunday',
'time' => '08:00',
'form' => 'normal',
),
1 =>
array (
'day' => 'Every Monday',
'time' => '10:30',
'form' => 'special',
),
)
int(2)

Find repeating values in multidimensional array

I'm trying to find when the array has 2nd dimension values that are the same so I can deal with them.
I've looked at array_unique and other people who are asking a similar question, but they all delete the values instead of returning them.
Say I have an array like this:
array(
[0] => array(
[laps] => 7,
[corrected_time] => 18
),
[1] => array(
[laps] => 6,
[corrected_time] => 18
),
[2] => array(
[laps] => 7,
[corrected_time] => 18.5
)
)
I'd like to have it return: array(0,1) because they both have the same value for corrected time
Here is one approach. First get the values for corrected_time and convert them to strings (because we'll use them in array_count_values, which only works on ints and strings).
$times = array_map('strval', array_column($your_array, 'corrected_time'));
Then find all the values that occur more than once using array_count_values and array_filter.
$repeats = array_filter(array_count_values($times), function($time) {
return $time > 1;
});
After you have this list of repeated times, you can use it to filter your original array to only include items with repeated times.
$multiples = array_filter($your_array, function($item) use ($repeats){
return isset($repeats[(string) $item['corrected_time']]);
});
You can iterate over this, or if you only want the keys, you can get them with
$keys = array_keys($multiples);

Increment the key on a multidimensional array

I have the following php array:
$month = array (
'weeks'=> array(
'w1'=> array(
'd1'=> 'day one',
'd2'=> 'day two',
'd3'=> array(
'H1' => 'hour one',
'H2' => 'hour two'
)
),
'w2'=> array(
'd1'=> 'day one',
'd2'=> 'day two'
)
)
);
I want to recursively iterate over the array and change the keys to reflect an incremented time from the start of the month. Like so (simplified):
$month = array (
'weeks'=> array(
'1'=> array(
'1'=> 'day one',
'2'=> 'day two',
'3'=> array(
'3' => 'hour one',
'4' => 'hour two'
)
),
'5'=> array(
'5'=> 'day one',
'6'=> 'day two'
)
)
);
Note: the counter is NOT incremented as it steps down to a child. This reflects that the start time for a Week and the start time for its First Day both share the same timestamp. The counter increments for the next sibling AND the counter increments after a 'key/value' pair has no children.
This is an example of 'nested arrays' with the output array exactly matching the structure of the input. The only difference is that the array keys are turned into 'almost' incrementing integer subscripts.
The 'almost' part comes from the fact that when 'nesting' down one level the first entry has the same subscript as the previous level.
The way i approached this is to use a recursive function, which will deal with the 'nesting', and a 'foreach' loop within each 'level as each entry in the array remains at the same 'level' in the output.
Output: This needs to contain many levels, rather than storing a path of subscripts to the current output 'level, i just pass an array 'by reference' that can be simply set by the appropriate code.
The code: running with the given input using PHP 5.3.19 at 'viper-7'
First the function that does the work:
Note: the $destWeeks and $idxNew variables are passed as 'references' this allows the code to amend them directly.
function reindexWeeks($sourceWeeks, &$destWeeks, &$idxNew)
{
foreach($sourceWeeks as $sourceKey => $sourceValue) {
if (is_array($sourceValue)) {
$destWeeks[$idxNew] = array();
reindexWeeks($sourceValue, $destWeeks[$idxNew], $idxNew);
}
else {
$destWeeks[$idxNew] = $sourceValue;
$idxNew++;
}
}
}
Calling code:
$idxNew = 1; // starting index.
$newWeeks = array(); // output array at the 'week' level
reindexWeeks($month['weeks'], $newWeeks, $idxNew); // re-index the array
// output array..
$newMonth = array('weeks' => $newWeeks);
Output using the example provided in the question:
Array
(
[weeks] => Array
(
[1] => Array
(
[1] => day one
[2] => day two
[3] => Array
(
[3] => hour one
[4] => hour two
)
)
[5] => Array
(
[5] => day one
[6] => day two
)
)
)

Fetching a multidimensional array

I am trying to edit a plugin that is fetching a multidimensional array, then breaking it out into a foreach statement and doing stuff with the resulting data.
What I am trying to do is edit the array before it gets to the foreach statement. I want to look and see if there is a key/value combination that exists, and if it does remove that entire subarray, then reform the array and pass it to a new variable.
The current variable
$arrayslides
returns several subarrays that look like something like this (I remove unimportant variables for the sake of briefness):
Array (
[0] => Array (
[slide_active] => 1
)
[1] => Array (
[slide_active] => 0
)
)
What I want to do is look and see if one of these subarrays contains the key slide_active with a value of 0. If it contains a value of zero, I want to dump the whole subarray altogether, then reform the multidimensional array back into the variable
$arrayslides
I have tried a few array functions but have not had any luck. Any suggestions?
$arrayslides = array(0 => array ( 'slide_active' => 1, 'other_data' => "Mark" ),
1 => array ( 'slide_active' => 0, 'other_data' => "ABCDE" ),
2 => array ( 'slide_active' => 1, 'other_data' => "Baker" ),
3 => array ( 'slide_active' => 0, 'other_data' => "FGHIJ" ),
);
$matches = array_filter($arrayslides, function($item) { return $item['slide_active'] == 1; } );
var_dump($matches);
PHP >= 5.3.0
I know its not so efficient but still
foreach ($arraySlides as $key => $value)
{
if(in_array('0', array_values($value))
unset($arraySlides[$key]);
}

Sorting an Array of Arrays in PHP --> Need a Good Algorithm

I have an array of 15000 elements each of which is an array of 4 elements. I want to sort by the second element of the 4. Originally I made the original array's keys the second element and then k-sorted but unfortunately, some of the second elements are duplicates and since one key can't refer to multiple elements i lost some elements in transition. I could bubble sort by the second element but I'm looking for something that runs at least on the order of nlog(n). Can anyone think of a good algorithm (or possibly a function in php that I don't know about) that can sort by the second element? Thank you!
I think you can use usort and define the cmp_function to use the second element.
As others have stated, usort or uasort to maintain the array keys is what you want:
<?php
$myArray = array(
'fruits' => array(
array('apples', 'oranges', 'bananas')
),
'vegetables' => array(
array('lettuce', 'carrots', 'peas')
),
'monkeys' => array(
array('Curious George', 'Amy', 'Marcel')
)
);
// PHP 5.3+ example using a closure
uasort($myArray, function(array $a, array $b) {
// Use whatever sorting algorithm you like
return strnatcasecmp($a[1], $b[1]);
});
var_export($myArray);
Running the above will output:
array (
'monkeys' =>
array (
0 =>
array (
0 => 'Curious George',
1 => 'Amy',
2 => 'Marcel',
),
),
'vegetables' =>
array (
0 =>
array (
0 => 'lettuce',
1 => 'carrots',
2 => 'peas',
),
),
'fruits' =>
array (
0 =>
array (
0 => 'apples',
1 => 'oranges',
2 => 'bananas',
),
),
)
Here's an example that doesn't use a closure for pre PHP 5.3:
sortFunc(array $a, array $b)
{
return strnatcasecmp($a[1], $b[1]);
}
uasort($myArray, 'sortFunc');
I don't know what the internal implementation of usort is, but I'd bet it's better than bubblesort (it's probably quicksort).

Categories