@@ -443,6 +443,197 @@ func TestPrefixSearchEmpty(t *testing.T) {
443443 }
444444}
445445
446+ func TestPrefixSearchIter (t * testing.T ) {
447+ trie := New [string ]()
448+
449+ // Add test data with values
450+ testData := map [string ]string {
451+ "foo" : "value1" ,
452+ "foosball" : "value2" ,
453+ "football" : "value3" ,
454+ "foreboding" : "value4" ,
455+ "forementioned" : "value5" ,
456+ "foretold" : "value6" ,
457+ "foreverandeverandeverandever" : "value7" ,
458+ "forbidden" : "value8" ,
459+ "bar" : "value9" ,
460+ "baz" : "value10" ,
461+ }
462+
463+ for key , value := range testData {
464+ trie .Add (key , value )
465+ }
466+
467+ tests := []struct {
468+ prefix string
469+ expected map [string ]string
470+ }{
471+ {
472+ prefix : "fo" ,
473+ expected : map [string ]string {
474+ "foo" : "value1" ,
475+ "foosball" : "value2" ,
476+ "football" : "value3" ,
477+ "foreboding" : "value4" ,
478+ "forementioned" : "value5" ,
479+ "foretold" : "value6" ,
480+ "foreverandeverandeverandever" : "value7" ,
481+ "forbidden" : "value8" ,
482+ },
483+ },
484+ {
485+ prefix : "foosbal" ,
486+ expected : map [string ]string {
487+ "foosball" : "value2" ,
488+ },
489+ },
490+ {
491+ prefix : "bar" ,
492+ expected : map [string ]string {
493+ "bar" : "value9" ,
494+ },
495+ },
496+ {
497+ prefix : "xyz" ,
498+ expected : map [string ]string {},
499+ },
500+ {
501+ prefix : "" ,
502+ expected : testData , // Empty prefix should return all entries
503+ },
504+ }
505+
506+ for _ , test := range tests {
507+ t .Run (test .prefix , func (t * testing.T ) {
508+ // Collect results from iterator
509+ iterResults := make (map [string ]string )
510+ for key , value := range trie .PrefixSearchIter (test .prefix ) {
511+ iterResults [key ] = value
512+ }
513+
514+ // Compare lengths
515+ if len (iterResults ) != len (test .expected ) {
516+ t .Errorf ("Length mismatch for prefix '%s': got %d, expected %d" ,
517+ test .prefix , len (iterResults ), len (test .expected ))
518+ }
519+
520+ // Compare key-value pairs
521+ for expectedKey , expectedValue := range test .expected {
522+ if actualValue , ok := iterResults [expectedKey ]; ! ok {
523+ t .Errorf ("Missing key '%s' for prefix '%s'" , expectedKey , test .prefix )
524+ } else if actualValue != expectedValue {
525+ t .Errorf ("Value mismatch for key '%s' with prefix '%s': got '%s', expected '%s'" ,
526+ expectedKey , test .prefix , actualValue , expectedValue )
527+ }
528+ }
529+
530+ // Check for unexpected keys
531+ for actualKey := range iterResults {
532+ if _ , ok := test .expected [actualKey ]; ! ok {
533+ t .Errorf ("Unexpected key '%s' for prefix '%s'" , actualKey , test .prefix )
534+ }
535+ }
536+ })
537+ }
538+ }
539+
540+ func TestPrefixSearchIterEmpty (t * testing.T ) {
541+ trie := New [string ]()
542+
543+ count := 0
544+ for range trie .PrefixSearchIter ("" ) {
545+ count ++
546+ }
547+
548+ if count != 0 {
549+ t .Errorf ("Expected 0 entries from empty trie, got: %d" , count )
550+ }
551+ }
552+
553+ func TestPrefixSearchIterEarlyStop (t * testing.T ) {
554+ trie := New [int ]()
555+ keys := []string {"foo" , "foobar" , "foobaz" , "football" , "foosball" }
556+ for i , key := range keys {
557+ trie .Add (key , i )
558+ }
559+
560+ // Test that we can stop iteration early
561+ count := 0
562+ maxCount := 2
563+ for range trie .PrefixSearchIter ("foo" ) {
564+ count ++
565+ if count >= maxCount {
566+ break
567+ }
568+ }
569+
570+ if count != maxCount {
571+ t .Errorf ("Expected to stop at %d iterations, got %d" , maxCount , count )
572+ }
573+ }
574+
575+ func TestPrefixSearchAndIterConsistency (t * testing.T ) {
576+ trie := New [int ]()
577+
578+ // Add test data
579+ testData := map [string ]int {
580+ "apple" : 1 ,
581+ "application" : 2 ,
582+ "apply" : 3 ,
583+ "banana" : 4 ,
584+ "band" : 5 ,
585+ "bandana" : 6 ,
586+ "can" : 7 ,
587+ "candy" : 8 ,
588+ "candid" : 9 ,
589+ }
590+
591+ for key , value := range testData {
592+ trie .Add (key , value )
593+ }
594+
595+ prefixes := []string {"" , "app" , "ban" , "can" , "z" }
596+
597+ for _ , prefix := range prefixes {
598+ t .Run (prefix , func (t * testing.T ) {
599+ // Get results from PrefixSearch
600+ searchResults := trie .PrefixSearch (prefix )
601+ searchSet := make (map [string ]bool )
602+ for _ , key := range searchResults {
603+ searchSet [key ] = true
604+ }
605+
606+ // Collect results from PrefixSearchIter
607+ iterResults := make (map [string ]int )
608+ for key , value := range trie .PrefixSearchIter (prefix ) {
609+ iterResults [key ] = value
610+ }
611+
612+ // Check that all keys match
613+ if len (searchResults ) != len (iterResults ) {
614+ t .Errorf ("Length mismatch for prefix '%s': PrefixSearch=%d, PrefixSearchIter=%d" ,
615+ prefix , len (searchResults ), len (iterResults ))
616+ }
617+
618+ // Verify all keys from PrefixSearch are in PrefixSearchIter
619+ for _ , key := range searchResults {
620+ if _ , ok := iterResults [key ]; ! ok {
621+ t .Errorf ("Key '%s' found in PrefixSearch but not in PrefixSearchIter for prefix '%s'" ,
622+ key , prefix )
623+ }
624+ }
625+
626+ // Verify all keys from PrefixSearchIter are in PrefixSearch
627+ for key := range iterResults {
628+ if ! searchSet [key ] {
629+ t .Errorf ("Key '%s' found in PrefixSearchIter but not in PrefixSearch for prefix '%s'" ,
630+ key , prefix )
631+ }
632+ }
633+ })
634+ }
635+ }
636+
446637func TestFuzzySearch (t * testing.T ) {
447638 setup := []string {
448639 "foosball" ,
@@ -657,6 +848,34 @@ func BenchmarkPrefixSearch(b *testing.B) {
657848 }
658849}
659850
851+ func BenchmarkPrefixSearchIter (b * testing.B ) {
852+ trie := createTrieAndAddFromFile [interface {}]("/usr/share/dict/words" , nil )
853+
854+ b .ResetTimer ()
855+ for i := 0 ; i < b .N ; i ++ {
856+ count := 0
857+ for range trie .PrefixSearchIter ("fo" ) {
858+ count ++
859+ }
860+ }
861+ }
862+
863+ func BenchmarkPrefixSearchIterEarlyStop (b * testing.B ) {
864+ trie := createTrieAndAddFromFile [interface {}]("/usr/share/dict/words" , nil )
865+
866+ b .ResetTimer ()
867+ for i := 0 ; i < b .N ; i ++ {
868+ count := 0
869+ maxCount := 10
870+ for range trie .PrefixSearchIter ("fo" ) {
871+ count ++
872+ if count >= maxCount {
873+ break
874+ }
875+ }
876+ }
877+ }
878+
660879func BenchmarkFuzzySearch (b * testing.B ) {
661880 trie := createTrieAndAddFromFile [interface {}]("fixtures/test.txt" , nil )
662881
0 commit comments