Reputation: 1512
I'm getting super confused between all the different array functions in PHP and I have no idea which is best to use in my scenario.
I have one array that lists user sign ups and one array which lists subscriptions for those users with the date the user subscribed. The user can have multiple subscriptions or not at all. An example would be:
$users = [
['user_id' => 1, 'sign_up_date' => '2020-01-01 00:00:00'],
['user_id' => 2, 'sign_up_date' => '2020-01-02 00:00:00'],
['user_id' => 3, 'sign_up_date' => '2020-01-03 00:00:00'],
...
];
$subscriptions = [
['user_id' => 1, 'subscription_category' => 'abc', 'sign_up_date' => '2020-01-01 00:01:00'],
['user_id' => 1, 'subscription_category' => 'def', 'sign_up_date' => '2020-01-01 00:02:00'],
['user_id' => 1, 'subscription_category' => 'ghi', 'sign_up_date' => '2020-01-02 00:03:00'],
['user_id' => 2, 'subscription_category' => 'jkl', 'sign_up_date' => '2020-01-02 00:01:00'],
['user_id' => 2, 'subscription_category' => 'mno', 'sign_up_date' => '2020-01-03 00:02:00'],
...
];
What I'm attempting to find is which users subscribed to any category on their sign up date. So in this case I want to find the number of items in:
$usersWhoSubscribedOnSignUpDate = [1, 2];
What I could do is something like:
$results = [];
foreach ($users as $user) {
foreach ($subscriptions as $subscription) {
$signUpDate = date('Y-m-d', strtotime($user['sign_up_date']));
$subscriptionDate = date('Y-m-d', strtotime($subscription['sign_up_date']));
if ($subscription['user_id'] === $subscription['user_id'] && $signUpDate === $subscriptionDate) {
$results[] = subscription['user_id'];
}
}
}
$results = array_unique($results);
But that's not very elegant in my eyes and I'm convinced that one of the many PHP array functions could simplify this process somehow.
array_intersect
doesn't seem suitable because it doesn't appear to work with multi-level arrays like this.
array_map
doesn't seem suitable because it doesn't have the ability to compare two arrays.
array_uintersect_assoc
seems like it could be an option however I can't understand what the "additional index check" means or how to sort the data. I believe that the callback function needs to return a comparison between two objects so perhaps this could be sorted by timestamp of the sign_up_date
property?
Upvotes: 1
Views: 72
Reputation: 3917
The first think I would do is modify your arrays so the users are identified by keys, (that's the first two lines). The third line reduces the subscription dates to the earliest for each user, then the fourth finds places where the sign up and earliest subscription dates match.
foreach( $users as $u ) { $signups[$u['user_id']] = date('Y-m-d', strtotime($u['sign_up_date']) ); }
foreach( $subscriptions as $s ) { $subs[$s['user_id']][] = date('Y-m-d', strtotime($s['sign_up_date']) ); }
foreach( $subs as $key => $value ) { $subs[$key] = min( $subs[$key] ); }
$results = array_keys( array_intersect_assoc( $signups, $subs ) );
Upvotes: 0
Reputation: 53591
You can do this in one iteration of $users
and one iteration of $subscriptions
, rather than one iteration of $subscriptions
per row in $users
. This could represent a significant performance improvement as the number of rows increase.
First, build an associative array of user signup dates indexed by user_id, so that you do not need to iterate over it every time you need to look up a date:
$userDates = [];
foreach ($users as $user) {
$userDates[$user['user_id']] = date('Y-m-d', strtotime($user['sign_up_date']);
}
This will give you:
Array
(
[1] => 2020-01-01
[2] => 2020-01-02
[3] => 2020-01-03
)
Then just iterate over the subscriptions once, looking up that user's signup date as you go:
foreach ($subscriptions as $subscription) {
if (
date('Y-m-d', strtotime($subscription['sign_up_date'])) ===
$userDates[$subscription['user_id']]
) {
...
}
}
Or perhaps something like:
array_unique(array_filter(
$subscriptions,
function ($subscription) use ($userDates) {
return date('Y-m-d', strtotime($subscription['sign_up_date'])) ===
$userDates[$subscription['user_id']];
}
));
Upvotes: 1