Skip to content

Commit 38ab634

Browse files
authored
Merge pull request #153 from voku/copilot/add-index-of-min-max
Add `findKey()` method to return index of first element matching a callable
2 parents e2d82c3 + f13314f commit 38ab634

5 files changed

Lines changed: 391 additions & 99 deletions

File tree

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@
1818
/phpunit.xml.dist export-ignore
1919
/psalm.xml export-ignore
2020
/renovate.json export-ignore
21+
/AGENTS.md export-ignore

AGENTS.md

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# AGENTS.md — Copilot Agent Knowledge Base for voku/Arrayy
2+
3+
This file documents project conventions, architecture, and findings from agent interactions. It is intended to help AI coding agents work effectively in this repository.
4+
5+
---
6+
7+
## Project Overview
8+
9+
- **Package**: `voku/arrayy`
10+
- **Description**: Array manipulation library for PHP
11+
- **License**: MIT
12+
- **Maintainer**: Lars Moelleken
13+
- **PHP requirement**: `>=8.0.0`
14+
- **Autoload namespace**: `Arrayy\``src/`
15+
- **Helper function namespace**: `src/Create.php` (defines global `a()` helper)
16+
17+
---
18+
19+
## Directory Structure
20+
21+
```
22+
src/
23+
Arrayy.php # Main class (~8000+ lines), all array methods live here
24+
ArrayyStrict.php # Strict-typed subclass of Arrayy
25+
ArrayyMeta.php # Meta/magic property helper (~67 lines)
26+
ArrayyIterator.php # Custom iterator
27+
ArrayyRewindableGenerator.php # Generator-backed iteration
28+
ArrayyRewindableExtendedGenerator.php
29+
Create.php # Global helper functions (a(), etc.)
30+
StaticArrayy.php # Static façade using __callStatic
31+
Collection/ # Collection base classes
32+
Type/ # Typed collections (StringCollection, ArrayCollection, etc.)
33+
TypeCheck/ # Type-checking utilities
34+
Mapper/ # JSON mapper
35+
36+
tests/
37+
ArrayyTest.php # Main test file (~6500+ lines), all tests in one class
38+
39+
build/
40+
generate_docs.php # Regenerates README.md from src/Arrayy.php + build/docs/base.md
41+
composer.json # Requires voku/php-readme-helper for doc generation
42+
docs/base.md # Base template for README generation
43+
44+
examples/
45+
JsonResponseDataExample.php
46+
```
47+
48+
---
49+
50+
## Running Tests
51+
52+
```bash
53+
php vendor/bin/phpunit --no-coverage
54+
```
55+
56+
- Tests use PHPUnit (supports `~6.0 || ~7.0 || ~9.0`)
57+
- All tests are in a single class `Arrayy\tests\ArrayyTest` in `tests/ArrayyTest.php`
58+
- Known pre-existing failures (unrelated to feature work): 2 errors (`array_sum` on strings) + 1 failure (sigma case)
59+
- Run targeted tests with `--filter "testMethodName"` to speed up iteration
60+
61+
---
62+
63+
## Regenerating README.md
64+
65+
The README is **auto-generated** — do not edit it manually.
66+
67+
```bash
68+
# Install build dependencies first (only needed once):
69+
cd build && composer install && cd ..
70+
71+
# Also install main project dependencies (needed by generate_docs.php):
72+
composer install --no-dev
73+
74+
# Generate README:
75+
php build/generate_docs.php
76+
```
77+
78+
This reads `src/Arrayy.php` docblocks and `build/docs/base.md`, then writes `README.md`.
79+
80+
---
81+
82+
## Adding New Methods
83+
84+
All public methods in `src/Arrayy.php` follow this pattern:
85+
86+
1. **PHPDoc block** with:
87+
- Description
88+
- `EXAMPLE: <code>...</code>` block (used in README generation)
89+
- `@param` and `@return` tags
90+
- `@phpstan-param` / `@phpstan-return` for generics (`TKey`, `T`)
91+
- `@psalm-mutation-free` if the method is immutable
92+
93+
2. **Method signature** — use `self` return type for immutable methods, `$this` for mutable
94+
95+
3. **Implementation** — prefer `$this->getGenerator()` for lazy iteration
96+
97+
4. **Test** in `tests/ArrayyTest.php`:
98+
- Add a `{method}Provider()` data provider method
99+
- Add a `test{Method}()` test method with `@dataProvider`
100+
- Place tests alphabetically relative to similar methods
101+
102+
5. **Rebuild README** by running `php build/generate_docs.php`
103+
104+
---
105+
106+
## Key Patterns
107+
108+
### Immutable vs Mutable
109+
- Immutable methods return `static` and create a new instance via `static::create()`
110+
- Mutable methods return `$this` and modify `$this->array` directly
111+
112+
### Generator-backed iteration
113+
- Use `$this->getGenerator()` instead of `$this->array` to support lazy/generator-backed arrays
114+
- Call `$this->generatorToArray()` first if you need direct array access
115+
116+
### Template types
117+
```php
118+
/**
119+
* @template TKey of array-key
120+
* @template T
121+
*/
122+
class Arrayy ...
123+
```
124+
Use `@phpstan-param T $value` and `@phpstan-return TKey|false` etc. for PHPStan generics.
125+
126+
### StaticArrayy
127+
`StaticArrayy` uses `__callStatic` to delegate all calls to an `Arrayy` instance — no changes needed there when adding instance methods.
128+
129+
---
130+
131+
## Findings from Task: `findKey()` (Issue #139)
132+
133+
**Problem**: No method existed to find the *key/index* of an array element by a predicate callable. The existing methods were:
134+
- `find(\Closure $closure)` — returns the matching **value**, or `false`
135+
- `searchIndex($value)` — returns the key for an exact **value** match
136+
- `indexOf($value)` — alias for `searchIndex()`
137+
138+
**Solution**: Added `findKey(\Closure $closure)` in `src/Arrayy.php` (after `find()`):
139+
- Accepts `\Closure` with signature `($value, $key): bool`
140+
- Iterates via `$this->getGenerator()` for generator compatibility
141+
- Returns the key of the first matching element, or `false`
142+
143+
**Usage examples**:
144+
```php
145+
// Find index of minimum value
146+
$minIdx = a([3, 1, 4, 1, 5, 9])->findKey(fn($v) => $v === 1); // 1
147+
148+
// Find by key
149+
a(['a' => 10, 'b' => 20])->findKey(fn($v, $k) => $k === 'b'); // 'b'
150+
151+
// Returns false when no match
152+
a([1, 2, 3])->findKey(fn($v) => $v === 99); // false
153+
```
154+
155+
**Test coverage added**:
156+
- `findKeyProvider()` — 11 data-driven cases covering empty arrays, booleans, integers, floats, strings, string keys, and no-match
157+
- `testFindKey()` — value-based matching via data provider
158+
- `testFindKeyWithKeyParameter()` — verifies `$key` is correctly forwarded to the closure
159+
160+
---
161+
162+
## .gitattributes
163+
164+
Files excluded from Composer distribution (`export-ignore`):
165+
- `/build`, `/examples`, `/tests`
166+
- Config files: `.editorconfig`, `.scrutinizer.yml`, `.styleci.yml`, etc.
167+
- `AGENTS.md` (this file)
168+
169+
---
170+
171+
## Dependencies
172+
173+
### Runtime
174+
- `symfony/polyfill-mbstring` ~1.0
175+
- `phpdocumentor/reflection-docblock` ~6.0
176+
177+
### Dev
178+
- `phpunit/phpunit` ~6.0 || ~7.0 || ~9.0
179+
- `phpstan/phpstan` ^2.0
180+
181+
### Build (doc generation only, in `build/`)
182+
- `voku/php-readme-helper` ~0.6

0 commit comments

Comments
 (0)