Коллекция Laravel фильтрует коллекцию по значениям из другой коллекции - PullRequest
0 голосов
/ 01 мая 2019

Я нахожусь в процессе создания пользовательского класса Collection, но у меня возникают некоторые проблемы с фильтрацией данных.

У меня есть набор финансовых данных ($ collection), который выглядит следующим образом:

App\CustomCollection {
    #items: array [
        0 => {
            +"financials": [
                0 => {
                    +"data_tag": {
                        +"tag": "revenue"
                    }
                    +"value": 1200
                }
            ]
            +"fundamental": {
                +"fiscal_year": 2018
                +"fiscal_period": "FY"
                +"start_date": "2018-01-01"
                +"end_date": "2018-12-31"
            }
            +"company": {#336
                +"id": "com_TEST1"
            }
        }
        1 => {
            +"financials": [
                0 => {
                    +"data_tag": {
                        +"tag": "revenue"
                    }
                    +"value": 300
                }
            ]
            +"fundamental": {
                +"fiscal_year": 2018
                +"fiscal_period": "Q1"
                +"end_date": "2018-03-31"
            }
            +"company": {#336
                +"name": "Test Company Inc"
            }
        }
        2 => {
            +"financials": [
                0 => {
                    +"data_tag": {
                        +"tag": "revenue"
                    }
                    +"value": 300
                }
            ]
            +"fundamental": {
                +"fiscal_year": 2018
                +"fiscal_period": "Q2"
                +"end_date": "2018-06-30"
            }
            +"company": {#336
                +"name": "Test Company Inc"
            }
        }

        ... etc to Q4
    ]
}

Я могу успешно вернуть свои финансовые годы со следующими данными:

$fiscalYears = $collection->where('fundamental.fiscal_period', 'FY');

Возвращает:

App\CustomCollection {
    #items: array [
        0 => {
            +"financials": [
                0 => {
                    +"data_tag": {
                        +"tag": "revenue"
                    }
                    +"value": 1200
                }
            ]
            +"fundamental": {
                +"fiscal_year": 2018
                +"fiscal_period": "FY"
                +"start_date": "2018-01-01"
                +"end_date": "2018-12-31"
            }
            +"company": {#336
                +"id": "com_TEST1"
            }
        }
    ]
}

Я также могу вернуть только четверти, выполнив:

$fiscalYears = $collection->where('fundamental.fiscal_period', '!=', 'FY');

App\CustomCollection {
    #items: array [
        0 => {
            +"financials": [
                0 => {
                    +"data_tag": {
                        +"tag": "revenue"
                    }
                    +"value": 300
                }
            ]
            +"fundamental": {
                +"fiscal_year": 2018
                +"fiscal_period": "Q1"
                +"end_date": "2018-03-31"
            }
            +"company": {#336
                +"name": "Test Company Inc"
            }
        }
        1 => {
            +"financials": [
                0 => {
                    +"data_tag": {
                        +"tag": "revenue"
                    }
                    +"value": 300
                }
            ]
            +"fundamental": {
                +"fiscal_year": 2018
                +"fiscal_period": "Q2"
                +"end_date": "2018-06-30"
            }
            +"company": {#336
                +"name": "Test Company Inc"
            }
        }

        ... etc to Q4
    ]
}

Здесь все замечательно и работает нормально, но теперь я пытаюсь сделать так, чтобы в $ fiscalYears возвращались только четверти, которые соответствуют определенным значениям.

Вот что я сейчас использую:

public function quartersByFiscalYears($fiscalYears)
    {
        $quarters =  [];
        foreach ($fiscalYears as $fiscalYear) {
            $quarters[] = $this->quarters()->where('company', $fiscalYear->company)
                ->where('fundamental.end_date', '>=', $fiscalYear->fundamental->start_date)
                ->where('fundamental.end_date', '<', $fiscalYear->fundamental->end_date)
                ->values('financials');
        }

        return $quarters;
    }

Самая важная часть приведенного выше кода состоит в том, что он возвращает только кварталы, в которых конечная дата $ квартала = начальная дата $ fiscalYear, а конечная дата $ квартала <конечная дата $ fiscalYear. </strong>

Это работает, но это самый медленный "фильтр" в моей коллекции. У меня такое ощущение, что я все это думаю неправильно, хотя я не уверен. Кажется, что циклически повторять всю коллекцию каждый раз, когда $ fiscalYears повторяется, - плохая идея Можно ли сделать это быстрее / эффективнее? Или foreach довольно распространен в этом сценарии?

1 Ответ

0 голосов
/ 01 мая 2019

Похоже, что каждая "группа" в ваших результирующих данных содержит кварталы для конкретной компании в данном году. Вы можете использовать красноречивые методы, чтобы сделать это НАМНОГО быстрее, чем проходить по кругу. Я проверил это с 40k записей. Зацикливание заняло ~ 45 секунд, но это займет меньше секунды.

$quarters = $collection->where('fundamental.fiscal_period', '!=', 'FY');

// use `groupBy()` to organize the quarters into the right chunks
$grouped = $quarters->groupBy(function($item) {
    // concatenate the company name and fiscal year to create the "group" name
    return $item->company->name.'_'.$item->fundamental->fiscal_year;
});

// use `$fiscalYears` with `map()` and `flip()` to create a collection 
// with the same key structure as $grouped
$filter = $fiscalYears->map(function($item) {
    return $item->company->name.'_'.$item->fundamental->fiscal_year;
})->flip();

// filter the collection with `intersectByKeys()`
$result = $grouped->intersectByKeys($filter);

// If you want, replace the keys with numeric indices and convert to array
$result = $result->values()->toArray();

Только будьте осторожны с тем, какие красноречивые методы вы используете. Если вы используете flatten(), этот метод использует рекурсивные циклы под капотом, поэтому он все равно будет медленным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...