> $validated * @return list> */ public function consolidate(array $validated): array { /** @var array> $byCategory */ $byCategory = []; foreach ($validated as $index => $suggestion) { $byCategory[$this->categoryKey($suggestion)][] = $index; } $drop = []; foreach ($byCategory as $indices) { foreach ($indices as $broadIndex) { if (isset($drop[$broadIndex])) { continue; } foreach ($indices as $narrowIndex) { if ($broadIndex === $narrowIndex || isset($drop[$narrowIndex])) { continue; } if ($this->subsumes($validated[$broadIndex], $validated[$narrowIndex])) { $drop[$narrowIndex] = true; } } } } return array_values(array_filter( $validated, fn (mixed $suggestion, int $index): bool => ! isset($drop[$index]), ARRAY_FILTER_USE_BOTH, )); } /** * Whether the broad suggestion's token already covers the narrow one: same * field, both `contains`, and the broad token is a (shorter) substring of * the narrow token. * * @param array $broad * @param array $narrow */ private function subsumes(array $broad, array $narrow): bool { if ($broad['match_field'] !== $narrow['match_field']) { return false; } if ($broad['match_operator'] !== 'contains' || $narrow['match_operator'] !== 'contains') { return false; } $broadToken = (string) $broad['match_token']; $narrowToken = (string) $narrow['match_token']; return $broadToken !== $narrowToken && str_contains($narrowToken, $broadToken); } /** * @param array $suggestion */ private function categoryKey(array $suggestion): string { if (! empty($suggestion['proposed_category_id'])) { return 'cat:'.$suggestion['proposed_category_id']; } $name = mb_strtolower(trim((string) ($suggestion['new_category_name'] ?? ''))); $direction = (string) ($suggestion['new_category_direction'] ?? ''); return 'new:'.$direction.':'.$name; } }