简体   繁体   English

Laravel Collection按其他集合中的值过滤集合

[英]Laravel Collection filter a collection by values from another collection

I'm in the process of building a custom Collection class but I'm having some issues on filtering data. 我正在构建自定义Collection类,但我在过滤数据方面遇到了一些问题。

I have a collection of financial data, ($collection) that looks like this: 我有一个财务数据集合($ 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
    ]
}

I can successfully return my Fiscal Years with the following: 我可以通过以下方式成功返回我的财政年度:

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

Returns: 返回:

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"
            }
        }
    ]
}

I can also return just the quarters by doing: 我也可以通过以下方式返回季度:

$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
    ]
}

Everything is great here and works fine, but now what I am trying to accomplish is to return only quarters that match specific values in $fiscalYears. 这里的一切都很棒并且工作正常,但现在我想要完成的只是返回与$ fiscalYears中的特定值匹配的四分之一。

Here is what I am using now: 这是我现在使用的:

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;
    }

The most important part of the code above is that it returns only quarters where the $quarter end_date >= the $fiscalYear start_date and where the $quarter end_date < the $fiscalYear end_date. 上面代码中最重要的部分是它只返回$ quarter end_date> = $ fiscalYear start_date和$ quarter end_date <$ fiscalYear end_date的四分之一。

This works, but it is by far the slowest "filter" in my collection. 这是有效的,但它是我收藏中迄今为止最慢的“过滤器”。 I have a feeling that I am thinking about this all wrong, although I'm not sure. 虽然我不确定,但我有一种感觉,我认为这一切都是错的。 It seems like looping over the entire collection every time $fiscalYears iterates is a bad idea. 每次$ fiscalYears迭代时,似乎循环遍历整个集合是一个坏主意。 Can this be done in a faster/more efficient way? 这可以更快/更有效的方式完成吗? Or is foreach pretty common in this scenario? 或者在这种情况下是非常常见的吗?

It looks like each "group" in your resulting data contains the quarters for a particular company in a given year. 看起来您的结果数据中的每个“组”都包含给定年份中特定公司的季度。 You can use eloquent methods to get this done MUCH faster than looping over the quarters. 您可以使用雄辩的方法来完成此任务,而不是在宿舍上循环。 I tested this with 40k records. 我用40k记录测试了这个。 Looping took ~45 secs, but this takes less than a second. 循环耗时约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();

Just be careful about which eloquent methods you use. 请注意您使用的雄辩方法。 If you use flatten() , that method uses recursive loops under the hood, so it will still be slow. 如果你使用flatten() ,那个方法使用引擎盖下的递归循环,所以它仍然会很慢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM