rbtree-ruby 0.3.5 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d89cd253c189b3342f30e2a4df3a293561e2353af6592d86f25fcea5eeb76433
4
- data.tar.gz: 9af04bff3851e7806fc637c2fc0ad583decf8e7c020edf841220a2b90fe8aaf4
3
+ metadata.gz: 18d0cdfd5f115b61096c266d9de0a482e263574aad1dfce425efaabd3dad67b7
4
+ data.tar.gz: 639a4f4f85c14cab4acf93f5b9b5280fb6225c96c575cac1a47b2b4f934dca8e
5
5
  SHA512:
6
- metadata.gz: a9708ffb8b421f883953c68582c607b9333e671542e21c0f3e0927838d2bb68202142150c11dd11541ea79fa1fcafc6c3195456c38d4b0db6b3897272d205835
7
- data.tar.gz: 601190e616b7b478cc6ceabe682e9d43ef169871753667fe61e326f65daceaa60441122a550f8458f1d4a7a794a032ac0add88f326956db903854ee96e34b613
6
+ metadata.gz: e40c1a174c7f2dd1c6df7f94e316070178457e0baa41322baa0f622157eb548245bfd334435488d3a49cfe8396e8d587ae80f886a17dadbf93cd9f03fcd701d7
7
+ data.tar.gz: 20f49f91135a98e4b46ccb4b0981dafab6bb35a9375bfb06024b7b42f5c71ad04b23613ad5a1f372ac6456d9edf03ef9698b68ca2228e0cf5cb85d6c2ae2b5f4
data/CHANGELOG.md CHANGED
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.3.6] - 2026-01-28
9
+
10
+ ### Added
11
+
12
+ > **Note:** All methods added in this release are **convenience methods** composed from existing public API primitives (`each`, `insert`, `delete`, `dup`, `merge!`, `safe: true`). They provide no speed advantage over manual composition — their value is purely in readability and API completeness.
13
+
14
+ - **`dup` (Deep Copy)**: `RBTree` and `MultiRBTree` now support `dup` and `clone` via `initialize_copy`. Creates an independent deep copy of the tree — modifications to the copy do not affect the original and vice versa.
15
+ - **`select`**: Returns a new tree containing only key-value pairs for which the block returns true. Returns an `Enumerator` if no block is given.
16
+ - **`reject`**: Returns a new tree excluding key-value pairs for which the block returns true. Returns an `Enumerator` if no block is given.
17
+ - **`delete_if`**: Deletes key-value pairs in place for which the block returns true. Returns self. In `MultiRBTree`, operates at individual value granularity (can remove specific values without deleting the entire key).
18
+ - **`reject!`**: Same as `delete_if`, but returns `nil` if no changes were made.
19
+ - **`keep_if`**: Keeps only key-value pairs for which the block returns true, deleting the rest in place. Returns self. In `MultiRBTree`, operates at individual value granularity.
20
+ - **`invert`**: Returns a new tree with keys and values swapped. For `RBTree`, duplicate values result in the last key winning (same as `Hash#invert`). For `MultiRBTree`, all pairs are preserved.
21
+ - **`merge`** (non-destructive): Returns a new tree with merged contents. Supports block for duplicate key resolution: `tree.merge(other) { |key, v1, v2| v1 }`.
22
+ - **`merge!` block support**: `merge!` now accepts a block for duplicate key resolution, matching `Hash#merge!` behavior.
23
+
24
+ ### Optimized
25
+ - **`MultiRBTree#delete_if` / `#keep_if`**: Replaced clear-and-rebuild approach with in-place value array filtering via internal `filter_values!`. Avoids O(n log n) tree reconstruction; now runs in O(n) by directly mutating each node's value array and removing only emptied nodes.
26
+
27
+ ### Fixed
28
+ - **`MultiRBTree#clear`**: Fixed `@value_count` not being reset when `clear` was called, which caused `size` to return incorrect values after clearing and re-inserting.
29
+
8
30
  ## [0.3.5] - 2026-01-26
9
31
 
10
32
  ### Optimized
@@ -230,6 +252,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
230
252
  - ASCII diagrams for tree rotation operations
231
253
  - MIT License (Copyright © 2026 Masahito Suzuki)
232
254
 
255
+ [0.4.0]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.4.0
233
256
  [0.3.5]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.5
234
257
  [0.3.4]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.4
235
258
  [0.3.3]: https://github.com/firelzrd/rbtree-ruby/releases/tag/v0.3.3
data/README.ja.md CHANGED
@@ -197,10 +197,58 @@ tree.to_a # => [[1, "one"], [2, "two"]]
197
197
  # ハッシュへの変換
198
198
  tree.to_h # => {1 => "one", 2 => "two"}
199
199
 
200
- # 他のツリー、ハッシュ、またはEnumerableの結合
200
+ # 結合(破壊的)
201
201
  other = {3 => 'three'}
202
202
  tree.merge!(other)
203
203
  tree.size # => 3
204
+
205
+ # 結合(非破壊) — 新しいツリーを返す
206
+ merged = tree.merge({4 => 'four'})
207
+
208
+ # ブロックで重複キーの解決
209
+ merged = tree.merge({1 => 'ONE'}) { |key, old_val, new_val| old_val }
210
+
211
+ # キーと値を入れ替え
212
+ tree = RBTree.new({1 => 'a', 2 => 'b', 3 => 'c'})
213
+ tree.invert.to_a # => [["a", 1], ["b", 2], ["c", 3]]
214
+ ```
215
+
216
+ ### フィルタリングとコピー
217
+
218
+ > **注記:** `dup`、`select`、`reject`、`delete_if`、`reject!`、`keep_if`、`invert`、`merge` は既存のプリミティブを組み合わせた利便性メソッドです。手動で同等の処理を書いた場合と比べて速度上の利点はありません。可読性とAPI互換性のために提供されています。
219
+
220
+ Rubyの慣習的なメソッドでツリーのコピーやフィルタリングが可能です:
221
+
222
+ ```ruby
223
+ tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
224
+
225
+ # ディープコピー — 元のツリーと独立
226
+ copy = tree.dup
227
+ copy.delete(1)
228
+ tree.size # => 4 (変更なし)
229
+
230
+ # select / reject — 新しいツリーを返す
231
+ evens = tree.select { |k, _| k.even? } # => {2=>"two", 4=>"four"}
232
+ odds = tree.reject { |k, _| k.even? } # => {1=>"one", 3=>"three"}
233
+
234
+ # delete_if / keep_if — 破壊的に変更
235
+ tree.delete_if { |k, _| k > 2 }
236
+ tree.to_a # => [[1, "one"], [2, "two"]]
237
+
238
+ # reject! — delete_ifと同様だが、変更がなければnilを返す
239
+ tree.reject! { |_, _| false } # => nil
240
+ ```
241
+
242
+ `MultiRBTree`では、`delete_if`と`keep_if`は個々の値単位で操作します:
243
+
244
+ ```ruby
245
+ tree = MultiRBTree.new
246
+ tree.insert(1, 'a')
247
+ tree.insert(1, 'b')
248
+ tree.insert(2, 'c')
249
+
250
+ tree.delete_if { |k, v| k == 1 && v == 'a' }
251
+ tree.to_a # => [[1, "b"], [2, "c"]] — 'a'のみ削除された
204
252
  ```
205
253
 
206
254
  ### MultiRBTree 値配列アクセス
data/README.md CHANGED
@@ -197,10 +197,58 @@ tree.to_a # => [[1, "one"], [2, "two"]]
197
197
  # Convert to Hash
198
198
  tree.to_h # => {1 => "one", 2 => "two"}
199
199
 
200
- # Merge another tree, hash, or enumerable
200
+ # Merge (destructive)
201
201
  other = {3 => 'three'}
202
202
  tree.merge!(other)
203
203
  tree.size # => 3
204
+
205
+ # Merge (non-destructive) — returns a new tree
206
+ merged = tree.merge({4 => 'four'})
207
+
208
+ # Merge with block for duplicate key resolution
209
+ merged = tree.merge({1 => 'ONE'}) { |key, old_val, new_val| old_val }
210
+
211
+ # Invert keys and values
212
+ tree = RBTree.new({1 => 'a', 2 => 'b', 3 => 'c'})
213
+ tree.invert.to_a # => [["a", 1], ["b", 2], ["c", 3]]
214
+ ```
215
+
216
+ ### Filtering and Copying
217
+
218
+ > **Note:** `dup`, `select`, `reject`, `delete_if`, `reject!`, `keep_if`, `invert`, and `merge` are convenience methods composed from existing primitives. They provide no speed advantage over manual composition — their value is in readability and API completeness.
219
+
220
+ Create copies or filter trees using familiar Ruby idioms:
221
+
222
+ ```ruby
223
+ tree = RBTree.new({1 => 'one', 2 => 'two', 3 => 'three', 4 => 'four'})
224
+
225
+ # Deep copy — independent of original
226
+ copy = tree.dup
227
+ copy.delete(1)
228
+ tree.size # => 4 (unchanged)
229
+
230
+ # select / reject — return a new tree
231
+ evens = tree.select { |k, _| k.even? } # => {2=>"two", 4=>"four"}
232
+ odds = tree.reject { |k, _| k.even? } # => {1=>"one", 3=>"three"}
233
+
234
+ # delete_if / keep_if — modify in place
235
+ tree.delete_if { |k, _| k > 2 }
236
+ tree.to_a # => [[1, "one"], [2, "two"]]
237
+
238
+ # reject! — like delete_if, but returns nil if nothing changed
239
+ tree.reject! { |_, _| false } # => nil
240
+ ```
241
+
242
+ For `MultiRBTree`, `delete_if` and `keep_if` operate at individual value granularity:
243
+
244
+ ```ruby
245
+ tree = MultiRBTree.new
246
+ tree.insert(1, 'a')
247
+ tree.insert(1, 'b')
248
+ tree.insert(2, 'c')
249
+
250
+ tree.delete_if { |k, v| k == 1 && v == 'a' }
251
+ tree.to_a # => [[1, "b"], [2, "c"]] — only 'a' was removed
204
252
  ```
205
253
 
206
254
  ### MultiRBTree Value Array Access
@@ -2,5 +2,5 @@
2
2
 
3
3
  class RBTree
4
4
  # The version of the rbtree-ruby gem
5
- VERSION = "0.3.5"
5
+ VERSION = "0.3.6"
6
6
  end
data/lib/rbtree.rb CHANGED
@@ -111,6 +111,16 @@ class RBTree
111
111
  end
112
112
  end
113
113
 
114
+ # Creates a deep copy of the tree.
115
+ # Called automatically by `dup` and `clone`.
116
+ #
117
+ # @param orig [RBTree] the original tree to copy
118
+ # @return [void]
119
+ def initialize_copy(orig)
120
+ initialize(overwrite: orig.instance_variable_get(:@overwrite))
121
+ orig.each { |k, v| insert(k, v) }
122
+ end
123
+
114
124
  # Returns a Hash containing all key-value pairs from the tree.
115
125
  #
116
126
  # @return [Hash] a new Hash with the tree's contents
@@ -369,16 +379,43 @@ class RBTree
369
379
  end
370
380
  alias :[]= :insert
371
381
 
382
+ # Returns a new tree containing the merged contents of self and other.
383
+ #
384
+ # When a block is given, it is called with (key, old_value, new_value) for
385
+ # duplicate keys, and the block's return value is used.
386
+ #
387
+ # @param other [RBTree, Hash, Enumerable] the source to merge from
388
+ # @yield [key, old_value, new_value] called for duplicate keys when block given
389
+ # @return [RBTree] a new tree with merged contents
390
+ def merge(other, &block)
391
+ dup.merge!(other, &block)
392
+ end
393
+
372
394
  # Merges the contents of another tree, hash, or enumerable into this tree.
373
395
  #
396
+ # When a block is given, it is called with (key, old_value, new_value) for
397
+ # duplicate keys, and the block's return value is used.
398
+ #
374
399
  # @param other [RBTree, Hash, Enumerable] the source to merge from
375
- # @param overwrite [Boolean] whether to overwrite existing keys (default: true)
400
+ # @param overwrite [Boolean] whether to overwrite existing keys (default: true). Ignored if block given.
401
+ # @yield [key, old_value, new_value] called for duplicate keys when block given
376
402
  # @return [RBTree] self
377
- def merge!(other, overwrite: true)
403
+ def merge!(other, overwrite: true, &block)
378
404
  if defined?(MultiRBTree) && other.is_a?(MultiRBTree)
379
405
  raise ArgumentError, "Cannot merge MultiRBTree into RBTree"
380
406
  end
381
- insert(other, overwrite: overwrite)
407
+ if block
408
+ other_enum = other.is_a?(Hash) || other.is_a?(RBTree) ? other : other.each
409
+ other_enum.each do |k, v|
410
+ if has_key?(k)
411
+ insert_entry(k, block.call(k, value(k), v), overwrite: true)
412
+ else
413
+ insert_entry(k, v)
414
+ end
415
+ end
416
+ else
417
+ insert(other, overwrite: overwrite)
418
+ end
382
419
  self
383
420
  end
384
421
 
@@ -602,6 +639,72 @@ class RBTree
602
639
  self
603
640
  end
604
641
 
642
+ # Returns a new tree containing key-value pairs for which the block returns true.
643
+ #
644
+ # @yield [key, value] each key-value pair
645
+ # @return [RBTree, Enumerator] a new tree with selected pairs, or Enumerator if no block
646
+ def select(&block)
647
+ return enum_for(__method__) { size } unless block_given?
648
+ result = self.class.new
649
+ each { |k, v| result.insert(k, v) if block.call(k, v) }
650
+ result
651
+ end
652
+
653
+ # Returns a new tree containing key-value pairs for which the block returns false.
654
+ #
655
+ # @yield [key, value] each key-value pair
656
+ # @return [RBTree, Enumerator] a new tree with non-rejected pairs, or Enumerator if no block
657
+ def reject(&block)
658
+ return enum_for(__method__) { size } unless block_given?
659
+ result = self.class.new
660
+ each { |k, v| result.insert(k, v) unless block.call(k, v) }
661
+ result
662
+ end
663
+
664
+ # Deletes key-value pairs for which the block returns true. Returns nil if no changes were made.
665
+ #
666
+ # @yield [key, value] each key-value pair
667
+ # @return [RBTree, nil, Enumerator] self if changed, nil if unchanged, or Enumerator if no block
668
+ def reject!(&block)
669
+ return enum_for(__method__) { size } unless block_given?
670
+ size_before = size
671
+ delete_if(&block)
672
+ size == size_before ? nil : self
673
+ end
674
+
675
+ # Keeps key-value pairs for which the block returns true, deleting the rest. Modifies the tree in place.
676
+ #
677
+ # @yield [key, value] each key-value pair
678
+ # @return [RBTree, Enumerator] self, or Enumerator if no block
679
+ def keep_if(&block)
680
+ return enum_for(__method__) { size } unless block_given?
681
+ each(safe: true) { |k, v| delete(k) unless block.call(k, v) }
682
+ self
683
+ end
684
+
685
+ # Deletes key-value pairs for which the block returns true. Modifies the tree in place.
686
+ #
687
+ # @yield [key, value] each key-value pair
688
+ # @return [RBTree, Enumerator] self, or Enumerator if no block
689
+ def delete_if(&block)
690
+ return enum_for(__method__) { size } unless block_given?
691
+ each(safe: true) { |k, v| delete(k) if block.call(k, v) }
692
+ self
693
+ end
694
+
695
+ # Returns a new tree with keys and values swapped.
696
+ #
697
+ # For RBTree, duplicate values result in later keys overwriting earlier ones.
698
+ # For MultiRBTree, all key-value pairs are preserved.
699
+ # Values must implement <=> to serve as keys in the new tree.
700
+ #
701
+ # @return [RBTree, MultiRBTree] a new tree with keys and values inverted
702
+ def invert
703
+ result = self.class.new
704
+ each { |k, v| result.insert(v, k) }
705
+ result
706
+ end
707
+
605
708
  # Returns a string representation of the tree.
606
709
  #
607
710
  # Shows the first 5 entries and total size. Useful for debugging.
@@ -1516,6 +1619,13 @@ class MultiRBTree < RBTree
1516
1619
  # Returns the number of values stored in the tree.
1517
1620
  # @return [Integer] the number of values in the tree
1518
1621
  def size = @value_count
1622
+
1623
+ # Removes all elements from the tree.
1624
+ # @return [MultiRBTree] self
1625
+ def clear
1626
+ @value_count = 0
1627
+ super
1628
+ end
1519
1629
 
1520
1630
  # Returns the minimum key-value pair without removing it.
1521
1631
  #
@@ -1718,9 +1828,46 @@ class MultiRBTree < RBTree
1718
1828
  [key, val]
1719
1829
  end
1720
1830
 
1831
+ # Keeps key-value pairs for which the block returns true, deleting the rest.
1832
+ # Unlike RBTree, this removes individual values rather than entire keys.
1833
+ #
1834
+ # @yield [key, value] each key-value pair
1835
+ # @return [MultiRBTree, Enumerator] self, or Enumerator if no block
1836
+ def keep_if(&block)
1837
+ return enum_for(__method__) { size } unless block_given?
1838
+ filter_values! { |k, v| block.call(k, v) }
1839
+ self
1840
+ end
1841
+
1842
+ # Deletes key-value pairs for which the block returns true.
1843
+ # Unlike RBTree, this removes individual values rather than entire keys.
1844
+ #
1845
+ # @yield [key, value] each key-value pair
1846
+ # @return [MultiRBTree, Enumerator] self, or Enumerator if no block
1847
+ def delete_if(&block)
1848
+ return enum_for(__method__) { size } unless block_given?
1849
+ filter_values! { |k, v| !block.call(k, v) }
1850
+ self
1851
+ end
1852
+
1721
1853
  # @!visibility private
1722
1854
  private
1723
1855
 
1856
+ # Filters values in-place across all nodes.
1857
+ # Keeps only values for which the block returns true.
1858
+ # Removes nodes whose value arrays become empty.
1859
+ # Updates @value_count accordingly.
1860
+ def filter_values!
1861
+ keys_to_delete = []
1862
+ @hash_index.each do |key, node|
1863
+ before = node.value.size
1864
+ node.value.select! { |v| yield key, v }
1865
+ @value_count -= before - node.value.size
1866
+ keys_to_delete << key if node.value.empty?
1867
+ end
1868
+ keys_to_delete.each { |k| delete_indexed_node(k) }
1869
+ end
1870
+
1724
1871
  # Inserts a value for the given key.
1725
1872
  #
1726
1873
  # If the key already exists, the value is appended to its list.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbtree-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masahito Suzuki