pop
pop

Reputation: 3714

Laravel: Filter collection on property, with a twist

I am looking for an elegant (and comprehensible in a year's time) solution for a collection filtering challenge. I am querying a log database and get some sort of the following data structure

| refId   | isDeleted   | loggedDate          |
|---------|-------------|---------------------|
| 1       | 0           | 2018-02-23 21:39:05 |
| 2       | 0           | 2018-02-23 21:39:05 |
| 3       | 0           | 2018-02-23 21:39:05 |
| 4       | 0           | 2018-02-23 21:39:05 |
| 2       | 1           | 2018-02-23 21:49:05 |
| 5       | 0           | 2018-02-23 21:49:05 |
| 6       | 1           | 2018-02-23 21:49:05 |

My objective is to filter out the entries which have both, isDeleted=0 && isDeleted=1, and to leave only those which are either isDeleted=0 or isDeleted=1. In this example I should end up with datasets of 1, 3, 4, 5 and 6.

Thx.

PS. The collection might hold up to 1000 entries, if you were to ask...

Upvotes: 0

Views: 1801

Answers (2)

pop
pop

Reputation: 3714

Thanks to Don't Panic advice, I've come up with the following chain.

$collection->groupBy('refId')
                ->filter( function($refGroup) {
                    if ( $refGroup->count() > 1 ) {

                        # 0 + 1
                        $length = $refGroup->count();
                        $sum = $refGroup->reduce(function($carry, $item){
                            return $carry + $item->isDeleted;
                        });

                        # all sum=0 refIds are OK (no deletions)
                        if ( $sum == 0 ) {
                            return $refGroup;
                        } else {
                            # all sum=1 are good if the length=1 (deleted once)
                            if ( $length == $sum ) {
                                return $refGroup;
                            } else {
                                # sum=1 and length more than 1? manipulated and deleted => ignore
                                return false;
                            }
                        }

                    } else {
                        return $refGroup;
                    }
                })
                ->flatten(1)
                ->unique('refId');

The first tests are OK. Will need to test it with different datasets, though.

Upvotes: 0

Don't Panic
Don't Panic

Reputation: 41810

Maybe you could group by refId and isDeleted and filter the groups that only have one entry.

$refIds = $collection->groupBy(['refId', 'isDeleted'])->filter(function($refGroup) {
    return count($refGroup) === 1;
});

I can't currently test this to verify it works, I just tried to come up with an approximation for how I would do this in SQL based on the Laravel docs.

The result would still be grouped though, so it might not be that handy depending on how you're using it.

Upvotes: 1

Related Questions