php - Laravel Collection 按来自另一个集合的值过滤集合
问题描述
我正在构建自定义 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;
}
上面代码中最重要的部分是它只返回 $quarter end_date >= $fiscalYear start_date 和 $quarter end_date < $fiscalYear end_date 的季度。
这可行,但它是迄今为止我收藏中最慢的“过滤器”。我有一种感觉,我认为这一切都是错误的,尽管我不确定。似乎每次 $fiscalYears 迭代时循环整个集合是一个坏主意。这可以以更快/更有效的方式完成吗?还是 foreach 在这种情况下很常见?
解决方案
看起来结果数据中的每个“组”都包含特定公司在给定年份的季度。您可以使用雄辩的方法来完成这项工作,这比遍历四分之一要快得多。我用 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()
,该方法在后台使用递归循环,因此它仍然会很慢。
推荐阅读
- python - 使用curve_fit拟合随x变化的参数
- javascript - 在进行 fetch 调用之前检查条件
- javascript - 如何在动态创建的行之间获取文本
- dax - 在文本字符串中查找特定单词和单独单词的 DAX 公式
- c - 自动变量,函数后的指针仍然得到它
- java - 如何在 intellij 中查看 .form 中的 GUI 代码?
- c++ - boost::iostreams::mapped_file_source::open 在 Windows 上导致退出代码 3,但在 Ubuntu 中有效
- fortran - Fortran 中的大整数阶乘函数,与 python 或 Haskell 一样高效
- javascript - Javascript,如何将标签属性中的所有值放入数组中?
- c++ - 链接 GLFW 的本地安装