Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ability to remove item #36

Merged
merged 3 commits into from
Oct 20, 2020
Merged

add ability to remove item #36

merged 3 commits into from
Oct 20, 2020

Conversation

etrombly
Copy link
Contributor

From limited testing this seems to work well. Had to add a partialeq constraint to filter data and points. I don't rebalance the tree, just remove the items, not sure if that would be helpful.
Fixes #30

Copy link
Owner

@mrhooray mrhooray left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@etrombly Thanks for the PR! Commented inline with a few questions.

src/kdtree.rs Outdated
self.left.as_mut().unwrap().remove(point, data)?;
}
}
self.size -= 1;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this always decreases the size, what if there's no match?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, I'll move the decrement to here "if let (Some(points), Some(bucket)) = (self.points.take(), self.bucket.take())"

src/kdtree.rs Show resolved Hide resolved
src/kdtree.rs Outdated
@@ -288,6 +288,25 @@ impl<A: Float + Zero + One, T, U: AsRef<[A]>> KdTree<A, T, U> {
}
}

pub fn remove(&mut self, point: &U, data: &T) -> Result<(), ErrorKind> {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the targeted use case for removing a node?

  1. by value(data)
  2. by coordinates(point)
  3. OR or BOTH relationship when both value and coordinates are provided

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed since you need the coordinate and value to create the tree, it would be reasonable to require them both to remove an item. It looked like since the values are stored separately that it would be difficult to remove them without having both.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems 1 & 2 are also quite common use case

  1. e.g. add/remove/update entity's location when data represents entity id/uuid
  2. e.g. when location(point) is actually the primary key for the use case

it's okay if we just want to implement 3's BOTH(AND) relationship but we have to make sure nodes are removed only when both point and data match the parameters

src/kdtree.rs Outdated
self.points = Some(points.into_iter().filter(|x| x != point).collect());
self.bucket = Some(bucket.into_iter().filter(|x| x != data).collect());
} else {
if self.right.is_some() {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we leverage split dimension and value similar to binary search?

src/kdtree.rs Outdated
return Err(err);
}
if let (Some(points), Some(bucket)) = (self.points.take(), self.bucket.take()) {
self.points = Some(points.into_iter().filter(|x| x != point).collect());
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trying to understand the safety of this implementation, what if point matches node A but data matches node B?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the incorrect point/data pair are passed in, there aren't any checks that they match. I didn't think there was a way to validate that. If they indices match we could use that, but I didn't think that was always true.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

related to the use case comment - iirc the points and buckets match by index; then we should be able to iterate by index to ensure both point and data match the input

src/lib.rs Outdated Show resolved Hide resolved
tests/kdtree.rs Outdated Show resolved Hide resolved
@etrombly
Copy link
Contributor Author

should have fixed most of the issues you brought up now, except binary search. Would you prefer if a point that isn't in the tree is passed to remove that it return an error? Right now it just doesn't remove anything.

tests/kdtree.rs Outdated
kdtree.add(&item3.0, item3.1).unwrap();
kdtree.add(&item4.0, item4.1).unwrap();

kdtree.remove(&&[1f64], &2).unwrap();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we're targeting use case 3(AND), maybe kdtree.remove(&item1.0, item2.1) would cover more scenarios?

@mrhooray
Copy link
Owner

thanks for the update - followed up on comments

re: return value - it'd be better if to indicate whether there're matches(possible to have duplicates) or not

  • maybe returning the number of the nodes that are removed, or the removed nodes for fidelity
  • it'd also help with internal implementation, e.g. updating size

src/kdtree.rs Outdated
return Err(err);
}
if let (Some(mut points), Some(mut bucket)) = (self.points.take(), self.bucket.take()) {
let point_pos = points.iter().position(|x| x == point);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could iterate by index to verify both point and data match
also current implementation short-circuits(position), which doesn't seem to handle the scenario with more than one match

@etrombly
Copy link
Contributor Author

I updated to match multiple values and return the number of items removed. If you'd like I can also add options to remove by data or point, instead of both.

@mrhooray mrhooray merged commit 2659643 into mrhooray:master Oct 20, 2020
@mrhooray
Copy link
Owner

Thanks for the PR! If you'd like to, feel free to iterate on binary-search(efficiency) and remove by data or point(functionality).

@etrombly etrombly deleted the remove branch October 20, 2020 13:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

remove points after add?
2 participants