This is an automated email from the ASF dual-hosted git repository.

tisonkun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datasketches-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new ff020a0  refactor(tuple): return named entries from iterators (#249)
ff020a0 is described below

commit ff020a0c375df48d89950f10db1b0fceb3309734
Author: tison <[email protected]>
AuthorDate: Mon Aug 31 01:30:44 2026 +0800

    refactor(tuple): return named entries from iterators (#249)
---
 CHANGELOG.md                                       |  2 +-
 datasketches/src/thetafamily/tuple/hash_table.rs   |  5 ++--
 datasketches/src/thetafamily/tuple/intersection.rs |  2 +-
 datasketches/src/thetafamily/tuple/mod.rs          |  1 +
 datasketches/src/thetafamily/tuple/sketch.rs       | 29 ++++++++++------------
 tests-integration/tests/serde_tests/tuple.rs       |  2 +-
 tests-integration/tests/tuple_test/a_not_b.rs      |  4 +--
 tests-integration/tests/tuple_test/intersection.rs |  2 +-
 tests-integration/tests/tuple_test/sketch.rs       | 16 ++++++++----
 tests-integration/tests/tuple_test/union.rs        |  4 +--
 10 files changed, 35 insertions(+), 32 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index aaf8efd..04d6c0c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,7 +16,7 @@ All significant changes to this project will be documented in 
this file.
 * Replace `FrequentItemsSketch::epsilon_for_lg` with the fallible 
`epsilon_for_max_map_size`, and change `apriori_error` to accept the same 
maximum map size plus an unsigned stream weight. These helpers now match the 
constructor's units, and `max_map_size` exposes the configured value.
 * Replace the `is_f32` flag on `TDigestMut::deserialize` with separate 
`deserialize` and `deserialize_f32` entry points, making the serialized 
precision explicit at the call site.
 * Remove `CpcUnion::num_coupons`, which exposed internal union state solely 
for tests. Inspect the resulting `CpcSketch` when diagnostics are needed.
-* Remove the `TupleEntry` re-export. Tuple sketch iterators already expose 
retained entries as `(hash, &summary)` pairs without leaking the private 
storage representation.
+* Tuple sketch iterators now yield `&TupleEntry<_>` values instead of `(hash, 
&summary)` pairs. Use `entry.hash()` and `entry.summary()` to inspect each 
retained entry.
 * `ThetaIntersection::to_sketch` and `TupleIntersection::to_sketch` now return 
`Option`. Callers must handle `None` until the intersection receives its first 
successful update.
 * `BloomFilterBuilder`, `ThetaSketchBuilder`, `ThetaUnionBuilder`, 
`TupleSketchBuilder`, and `TupleUnionBuilder` now validate their configuration 
when `build` is called, and `build` returns `Result`. Callers must propagate or 
handle construction errors.
 * `BloomFilterBuilder::{MIN_NUM_BITS, MAX_NUM_BITS, MIN_NUM_HASHES, 
MAX_NUM_HASHES}` are no longer public. Callers should pass configurations to 
`build` and handle `InvalidArgument` instead of prevalidating against these 
constants.
diff --git a/datasketches/src/thetafamily/tuple/hash_table.rs 
b/datasketches/src/thetafamily/tuple/hash_table.rs
index 638d0c7..1e84959 100644
--- a/datasketches/src/thetafamily/tuple/hash_table.rs
+++ b/datasketches/src/thetafamily/tuple/hash_table.rs
@@ -101,10 +101,9 @@ impl<S> TupleHashTable<S> {
         })
     }
 
-    /// Returns an iterator over retained entries as `(hash, &summary)` pairs.
-    pub fn iter(&self) -> impl Iterator<Item = (u64, &S)> + '_ {
+    /// Returns an iterator over retained entries.
+    pub fn iter(&self) -> impl Iterator<Item = &TupleEntry<S>> + '_ {
         self.iter_entries()
-            .map(|entry| (entry.hash.get(), &entry.summary))
     }
 }
 
diff --git a/datasketches/src/thetafamily/tuple/intersection.rs 
b/datasketches/src/thetafamily/tuple/intersection.rs
index b5e2e43..a38ca05 100644
--- a/datasketches/src/thetafamily/tuple/intersection.rs
+++ b/datasketches/src/thetafamily/tuple/intersection.rs
@@ -82,7 +82,7 @@ use crate::tuple::sketch::TupleSketchView;
 ///
 /// let result = intersection.to_sketch(true).unwrap();
 /// assert_eq!(result.num_retained(), 1); // only "shared"
-/// assert_eq!(result.iter().next().unwrap().1, &7); // 3 + 4
+/// assert_eq!(result.iter().next().unwrap().summary(), &7); // 3 + 4
 /// ```
 #[derive(Debug)]
 pub struct TupleIntersection<P>
diff --git a/datasketches/src/thetafamily/tuple/mod.rs 
b/datasketches/src/thetafamily/tuple/mod.rs
index 9c3fcf1..c057ee5 100644
--- a/datasketches/src/thetafamily/tuple/mod.rs
+++ b/datasketches/src/thetafamily/tuple/mod.rs
@@ -50,6 +50,7 @@ mod sketch;
 mod union;
 
 pub use self::a_not_b::TupleANotB;
+pub use self::hash_table::TupleEntry;
 pub use self::intersection::TupleIntersection;
 pub use self::jaccard_similarity::TupleJaccardSimilarity;
 pub use self::policy::DefaultUnionPolicy;
diff --git a/datasketches/src/thetafamily/tuple/sketch.rs 
b/datasketches/src/thetafamily/tuple/sketch.rs
index e13a462..22e924f 100644
--- a/datasketches/src/thetafamily/tuple/sketch.rs
+++ b/datasketches/src/thetafamily/tuple/sketch.rs
@@ -74,7 +74,7 @@ use crate::tuple::serialization::TupleSummaryValue;
 ///     .unwrap();
 /// sketch.update("apple", 1);
 /// let view = sketch.as_view();
-/// assert_eq!(view.iter().next().unwrap().1, &1);
+/// assert_eq!(view.iter().next().unwrap().summary(), &1);
 /// ```
 #[derive(Debug)]
 pub struct TupleSketchView<'a, S>(TupleSketchViewState<'a, S>);
@@ -91,12 +91,12 @@ enum TupleSketchIter<'a, S> {
 }
 
 impl<'a, S> Iterator for TupleSketchIter<'a, S> {
-    type Item = (u64, &'a S);
+    type Item = &'a TupleEntry<S>;
 
     fn next(&mut self) -> Option<Self::Item> {
         match self {
-            Self::Mutable(iter) => iter.next().map(|entry| (entry.hash(), 
entry.summary())),
-            Self::Compact(iter) => iter.next().map(|entry| (entry.hash(), 
entry.summary())),
+            Self::Mutable(iter) => iter.next(),
+            Self::Compact(iter) => iter.next(),
         }
     }
 
@@ -157,8 +157,8 @@ impl<'a, S> TupleSketchView<'a, S> {
         }
     }
 
-    /// Returns an iterator over retained hashes and borrowed summaries.
-    pub fn iter(self) -> impl Iterator<Item = (u64, &'a S)> + 'a {
+    /// Returns an iterator over retained entries.
+    pub fn iter(self) -> impl Iterator<Item = &'a TupleEntry<S>> + 'a {
         match self.0 {
             TupleSketchViewState::Mutable(table) => 
TupleSketchIter::Mutable(table.iter_entries()),
             TupleSketchViewState::Compact(sketch) => {
@@ -188,7 +188,7 @@ impl<S> KeySketch for TupleSketchView<'_, S> {
     }
 
     fn hashes(self) -> impl Iterator<Item = u64> {
-        self.iter().map(|(hash, _)| hash)
+        self.iter().map(TupleEntry::hash)
     }
 }
 
@@ -199,8 +199,7 @@ where
     type Entry = TupleEntry<S>;
 
     fn entries(self) -> impl Iterator<Item = Self::Entry> {
-        self.iter()
-            .map(|(hash, summary)| TupleEntry::new(hash, summary.clone()))
+        self.iter().cloned()
     }
 }
 
@@ -345,8 +344,8 @@ where
         self.table.reset();
     }
 
-    /// Returns an iterator over retained entries as `(hash, &summary)` pairs.
-    pub fn iter(&self) -> impl Iterator<Item = (u64, &P::Summary)> + '_ {
+    /// Returns an iterator over retained entries.
+    pub fn iter(&self) -> impl Iterator<Item = &TupleEntry<P::Summary>> + '_ {
         self.table.iter()
     }
 
@@ -495,11 +494,9 @@ impl<S> CompactTupleSketch<S> {
         self.seed_hash
     }
 
-    /// Returns an iterator over retained entries as `(hash, &summary)` pairs.
-    pub fn iter(&self) -> impl Iterator<Item = (u64, &S)> + '_ {
-        self.entries
-            .iter()
-            .map(|entry| (entry.hash(), entry.summary()))
+    /// Returns an iterator over retained entries.
+    pub fn iter(&self) -> impl Iterator<Item = &TupleEntry<S>> + '_ {
+        self.entries.iter()
     }
 
     /// Returns the approximate lower error bound given the number of standard 
deviations.
diff --git a/tests-integration/tests/serde_tests/tuple.rs 
b/tests-integration/tests/serde_tests/tuple.rs
index 50dcea9..fe4c7be 100644
--- a/tests-integration/tests/serde_tests/tuple.rs
+++ b/tests-integration/tests/serde_tests/tuple.rs
@@ -122,7 +122,7 @@ fn round_trip_preserves_summaries() {
         
CompactTupleSketch::<u64>::deserialize(&sketch.compact(true).serialize()).unwrap();
 
     assert_eq!(restored.num_retained(), 50);
-    let summaries: Vec<_> = restored.iter().map(|(_, &summary)| 
summary).collect();
+    let summaries: Vec<_> = restored.iter().map(|entry| 
*entry.summary()).collect();
     assert_that!(summaries, each(eq(&3)));
 }
 
diff --git a/tests-integration/tests/tuple_test/a_not_b.rs 
b/tests-integration/tests/tuple_test/a_not_b.rs
index f766136..397d957 100644
--- a/tests-integration/tests/tuple_test/a_not_b.rs
+++ b/tests-integration/tests/tuple_test/a_not_b.rs
@@ -30,7 +30,7 @@ use crate::tuple_sketch_with_range;
 fn sorted_entries(sketch: &CompactTupleSketch<u64>) -> Vec<(u64, u64)> {
     let mut entries: Vec<_> = sketch
         .iter()
-        .map(|(hash, &summary)| (hash, summary))
+        .map(|entry| (entry.hash(), *entry.summary()))
         .collect();
     entries.sort_unstable();
     entries
@@ -49,7 +49,7 @@ fn difference_keeps_only_a_summaries() {
 
     assert_eq!(result.num_retained(), 1);
     assert_eq!(result.estimate(), 1.0);
-    assert_eq!(result.iter().next().unwrap().1, &5);
+    assert_eq!(result.iter().next().unwrap().summary(), &5);
 }
 
 #[test]
diff --git a/tests-integration/tests/tuple_test/intersection.rs 
b/tests-integration/tests/tuple_test/intersection.rs
index 06cd40b..9caa0eb 100644
--- a/tests-integration/tests/tuple_test/intersection.rs
+++ b/tests-integration/tests/tuple_test/intersection.rs
@@ -77,7 +77,7 @@ fn overlap_combines_summaries() {
     let result = intersection.to_sketch(true).unwrap();
 
     assert_eq!(result.num_retained(), 1);
-    assert_eq!(result.iter().next().unwrap().1, &7);
+    assert_eq!(result.iter().next().unwrap().summary(), &7);
 }
 
 #[test]
diff --git a/tests-integration/tests/tuple_test/sketch.rs 
b/tests-integration/tests/tuple_test/sketch.rs
index e48a897..a4d858f 100644
--- a/tests-integration/tests/tuple_test/sketch.rs
+++ b/tests-integration/tests/tuple_test/sketch.rs
@@ -22,6 +22,7 @@ use datasketches::tuple::CompactTupleSketch;
 use datasketches::tuple::DefaultUpdatePolicy;
 use datasketches::tuple::SummaryPolicy;
 use datasketches::tuple::SummaryUpdatePolicy;
+use datasketches::tuple::TupleEntry;
 use datasketches::tuple::TupleSketch;
 use datasketches::tuple::TupleSketchBuilder;
 use googletest::assert_that;
@@ -60,7 +61,7 @@ fn updates_distinct_keys_and_accumulates_summaries() {
     assert_eq!(sketch.estimate(), 2.0);
     assert_eq!(sketch.num_retained(), 2);
 
-    let mut summaries: Vec<u64> = sketch.iter().map(|(_, &summary)| 
summary).collect();
+    let mut summaries: Vec<u64> = sketch.iter().map(|entry| 
*entry.summary()).collect();
     summaries.sort_unstable();
     assert_eq!(summaries, [5, 7]);
 }
@@ -86,7 +87,7 @@ fn default_update_policy_accepts_distinct_rhs_type() {
     sketch.update("key", "hello");
     sketch.update("key", " world");
 
-    assert_eq!(sketch.iter().next().unwrap().1, "hello world");
+    assert_eq!(sketch.iter().next().unwrap().summary(), "hello world");
 }
 
 struct ArraySumPolicy {
@@ -123,7 +124,10 @@ fn 
custom_update_policy_accepts_multiple_value_representations() {
     sketch.update("key", vec![3.0, 4.0]);
 
     assert_eq!(sketch.num_retained(), 1);
-    assert_eq!(sketch.iter().next().unwrap().1.as_slice(), [4.0, 6.0]);
+    assert_eq!(
+        sketch.iter().next().unwrap().summary().as_slice(),
+        [4.0, 6.0]
+    );
 }
 
 #[test]
@@ -185,8 +189,10 @@ fn empty_sampled_sketch_has_zero_bounds() {
     assert_eq!(sketch.upper_bound(NumStdDev::Three), 0.0);
 }
 
-fn sorted_entries<'a>(entries: impl Iterator<Item = (u64, &'a u64)>) -> 
Vec<(u64, u64)> {
-    let mut entries: Vec<_> = entries.map(|(hash, &summary)| (hash, 
summary)).collect();
+fn sorted_entries<'a>(entries: impl Iterator<Item = &'a TupleEntry<u64>>) -> 
Vec<(u64, u64)> {
+    let mut entries: Vec<_> = entries
+        .map(|entry| (entry.hash(), *entry.summary()))
+        .collect();
     entries.sort_unstable();
     entries
 }
diff --git a/tests-integration/tests/tuple_test/union.rs 
b/tests-integration/tests/tuple_test/union.rs
index 3724b08..c29b542 100644
--- a/tests-integration/tests/tuple_test/union.rs
+++ b/tests-integration/tests/tuple_test/union.rs
@@ -66,7 +66,7 @@ fn union_combines_overlapping_summaries() {
     union.update(&b).unwrap();
     let result = union.to_sketch(true);
 
-    let mut summaries: Vec<u64> = result.iter().map(|(_, &summary)| 
summary).collect();
+    let mut summaries: Vec<u64> = result.iter().map(|entry| 
*entry.summary()).collect();
     summaries.sort_unstable();
     assert_eq!(result.num_retained(), 3);
     assert_eq!(summaries, [1, 1, 7]);
@@ -137,7 +137,7 @@ fn custom_combine_policy_controls_overlapping_summaries() {
     union.update(&a).unwrap();
     union.update(&b).unwrap();
 
-    assert_eq!(union.to_sketch(true).iter().next().unwrap().1, &9);
+    assert_eq!(union.to_sketch(true).iter().next().unwrap().summary(), &9);
 }
 
 #[test]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to