PHP Как сгруппировать многомерный массив по ассоциативному значению ключа

#php #arrays

#php #массивы

Вопрос:

У меня есть этот текущий PHP-массив

 $newData = 

  array (size=3)
    0 => 
      array (size=6)
        'company_name' => string 'CO 1' (length=32)
        'year_and_filing_date' => string '2019' (length=4)
        'total_revenue' => string '200' (length=3)
        'net_income' => string '300' (length=3)
        'net_cash_ending_balance' => string '150' (length=3)
        'long-term_debt' => string '210' (length=3)
    1 => 
      array (size=6)
        'company_name' => string 'CO 1' (length=32)
        'year_and_filing_date' => string '2020' (length=4)
        'total_revenue' => string '200' (length=3)
        'net_income' => string '300' (length=3)
        'net_cash_ending_balance' => string '150' (length=3)
        'long-term_debt' => string '210' (length=3)
    2 => 
      array (size=6)
        'company_name' => string 'CO 1' (length=32)
        'year_and_filing_date' => string '2020' (length=4)
        'total_revenue' => string '100' (length=3)
        'net_income' => string '200' (length=3)
        'net_cash_ending_balance' => string '50' (length=2)
        'long-term_debt' => string '110' (length=3)
 

Я хочу объединить все массивы с аналогичными 'year_and_filling_date' и суммировать все
свойства 'total_revenue' , 'net_income' , 'net_cash_ending_balance' и 'long-term_debt'

Я не уверен, с чего начать,

Я подумываю о циклическом переходе к каждому массиву, чтобы получить уникальный ‘year_and_filing_date’

 $unique_years = array_unique(
   array_map(function($elem){
      return $elem['year_and_filing_date'];
   },
   $newData)
);
 

результат ['2019','2020'];

А затем сделайте еще один цикл, чтобы отфильтровать $newData результат ['2019','2020']

Я ожидаю получить два разных массива, один из которых возвращает 1 результат, а другой возвращает 2 результата,

Но как мне суммировать только выбранные поля?

Комментарии:

1. «Но как мне суммировать только выбранные поля?» Что вы имеете в виду как? Вы просто добавляете их вместе. Создайте новый пустой массив , выполните цикл по вашему $newData -array и проверьте, существует ли индекс значения в year_and_filing_date вашем новом массиве, если он не просто копирует выбранные значения, в противном случае добавьте их . Дополнительно: как вы получаете этот массив? Через базу данных? Возможно, вы могли бы вместо этого использовать групповые функции.

Ответ №1:

Вам не нужно фильтровать, вы можете проверить, существует ли запись с этим годом в цикле foreach

 $result = [];
foreach ($newData as $row) {
    $year = $row['year_and_filing_date'];
    if(!isset($result[$year])) {
        $result[$year]['total_revenue'] = $row['total_revenue'];
        $result[$year]['net_income'] = $row['net_income'];
        $result[$year]['net_cash_ending_balance'] = $row['net_cash_ending_balance'];
        $result[$year]['long-term_debt'] = $row['long-term_debt'];
        $result[$year]['last_scraped'] = date("Y-m-d h:i:s");
    } else {
        $result[$year]['total_revenue']  = $row['total_revenue'];
        $result[$year]['net_income']  = $row['net_income'];
        $result[$year]['net_cash_ending_balance']  = $row['net_cash_ending_balance'];
        $result[$year]['long-term_debt']  = $row['long-term_debt'];
        $result[$year]['last_scraped'] = date("Y-m-d h:i:s");        
    }
}
 

Ответ №2:

Это то, что я делал до сих пор, это работает, но не оптимизировано, я надеюсь, что кто-нибудь сможет улучшить мой код,

 $newData = [];

/* Transform the Filling Date to Year */
array_walk($data, 
    function ($item2, $key) use (amp;$newData)
    {
        foreach($item2 as $keyAssoc => $val)
        {
            if($keyAssoc == 'year_and_filing_date')
            {
                if(!empty($val))
                {
                    $newData[$key][$keyAssoc] = date("Y", strtotime(date("d/m/Y", strtotime($val))));
                }
            }else{
                $newData[$key][$keyAssoc] = $val;
            }
        }
    }
);

/* Group Data by Years and Sum Up */
if(count($newData)>0)
{
    /* Get Unique Years */
    $unique_years = array_unique(array_map(function($elem){return $elem['year_and_filing_date'];}, $newData));
    asort($unique_years);

    $consolidatedData = [];
    foreach($unique_years as $key => $val)
    {
        foreach($newData as $k => $propertyName)
        {
            /* Compare if Year is Matching */
            if($propertyName['year_and_filing_date'] == $val)
            {
                
                $filteredData = array_filter($consolidatedData, function ($item) use ($val){
                    return $item['year_and_filing_date'] === $val;
                });

                /* Already Exists */
                if(count($filteredData)>0)
                {
                    $consolidatedData[$val]['company_name'] = $propertyName['company_name'];
                    $consolidatedData[$val]['year_and_filing_date'] = $val;
                    $total_revenue_raw = $filteredData[$val]['total_revenue']   $propertyName['total_revenue'];
                    $consolidatedData[$val]['total_revenue'] = (string) $total_revenue_raw;
                    $net_income_raw = $filteredData[$val]['net_income']   $propertyName['net_income'];
                    $consolidatedData[$val]['net_income'] = (string) $net_income_raw;
                    $net_cash_ending_balance_raw = $filteredData[$val]['net_cash_ending_balance']   $propertyName['net_cash_ending_balance'];
                    $consolidatedData[$val]['net_cash_ending_balance'] = (string) $net_cash_ending_balance_raw;
                    $long_debt_raw = $filteredData[$val]['long-term_debt']   $propertyName['long-term_debt'];
                    $consolidatedData[$val]['long-term_debt'] = (string) $long_debt_raw;
                    $consolidatedData[$val]['source_url'] = $propertyName['source_url'];
                    $consolidatedData[$val]['last_scraped'] = date("Y-m-d h:i:s");
                }else{
                    $consolidatedData[$val]['company_name'] = $propertyName['company_name'];
                    $consolidatedData[$val]['year_and_filing_date'] = $val;
                    $consolidatedData[$val]['total_revenue'] = $propertyName['total_revenue'];
                    $consolidatedData[$val]['net_income'] = $propertyName['net_income'];
                    $consolidatedData[$val]['net_cash_ending_balance'] =$propertyName['net_cash_ending_balance'];
                    $consolidatedData[$val]['long-term_debt'] = $propertyName['long-term_debt'];
                    $consolidatedData[$val]['source_url'] = $propertyName['source_url'];
                    $consolidatedData[$val]['last_scraped'] = date("Y-m-d h:i:s");
                }
            }
        }
    }
}

return var_dump($consolidatedData);
 

Ответ №3:

Небольшая модификация решения от alibek.gao.

Ввод:

 $newData = [
       ['company_name' => 'CO 1' , 'year_and_filing_date' => '2019', 'total_revenue' => '200',
        'net_income' => '300' , 'net_cash_ending_balance' => '150' ,  'long-term_debt' =>  '210'
       ],
       ['company_name' => 'CO 1', 'year_and_filing_date' => '2020', 'total_revenue' => '200',
        'net_income' => '300','net_cash_ending_balance' => '150',   'long-term_debt' => '210'
       ],
        ['company_name' => 'CO 1', 'year_and_filing_date' =>'2020',  'total_revenue' => '100',
        'net_income' => '200', 'net_cash_ending_balance' => '50',   'long-term_debt' => '110',
        ]
];
 

Алгоритм можно легко записать в виде функции.

 $aggregateSum = ['total_revenue','net_income','net_cash_ending_balance','long-term_debt'];

$groupBy = 'year_and_filing_date';

$result = [];
foreach ($newData as $row) {
    $year = $row[$groupBy];
    if(!isset($result[$year])) {
        $result[$year] = $row;
    } else {
        foreach($aggregateSum as $field){
          $result[$year][$field]  = $row[$field];
        }
    }
}
 

В этом решении сохраняются не суммируемые поля:

Результат:

 array (
  2019 => 
  array (
    'company_name' => "CO 1",
    'year_and_filing_date' => "2019",
    'total_revenue' => "200",
    'net_income' => "300",
    'net_cash_ending_balance' => "150",
    'long-term_debt' => "210",
  ),
  2020 => 
  array (
    'company_name' => "CO 1",
    'year_and_filing_date' => "2020",
    'total_revenue' => 300,
    'net_income' => 500,
    'net_cash_ending_balance' => 200,
    'long-term_debt' => 320,
  ),
)