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

fix(set): map uniqueness #114

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

abenerd
Copy link

@abenerd abenerd commented Jan 1, 2023

Description

When mapping over a set it is possible to introduce duplicate values which is undesirable.

$set = new \Ramsey\Collection\Set('integer', [1, 2, 3]);
$set->map(fn () => 42);

Motivation and context

This change is so that the behavior of the implementation is respected such as in this case the duplicate values in a Set.

How has this been tested?

Tests have been added for the Set and Collection implementations that a instance of themselves is returned and that when mapping over an existing Set not duplicate values can be introduced.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
    The actual return types as declared have not changed but in the case of the Set a new Set is now returned instead of a Collection as well as the new mapping behavior for the Set that does now not allow duplicate values anymore.

PR checklist

  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING.md document.
  • I have added tests to cover my changes.

allows the configuration of the collection instance when mapping over
values so implementation specific behaviours are retained

BREAKING CHANGE: return types have changed
@abenerd abenerd requested a review from ramsey as a code owner January 1, 2023 09:36
@SimoTod
Copy link
Contributor

SimoTod commented Jan 1, 2023

I'm not sure map was meant to preserve the class implementation. It just maps all the values to a generic collection (you can also see the value type is mixed instead of using the current type). It will likely break things for a few projects.

@abenerd
Copy link
Author

abenerd commented Jan 1, 2023

I see the BC concern but I also feel like when I'm manipulating a set I want it to behave like a set. Maybe this can be merged into a v3 branch but I think this is an important change.

@SimoTod
Copy link
Contributor

SimoTod commented Jan 1, 2023

Laravel's collections have a mapInto function accepting the collection class name as a second parameter. If the maintainer is open to something like that maybe it could be a good compromise for your use case.

@ramsey
Copy link
Owner

ramsey commented Jan 1, 2023

I think the best approach would be to override the map() method on the Set class, rather than introducing the protected $collection property.

That said, map() returns a CollectionInterface. It's not intended to make guarantees about the data it returns. The library is designed to be generic and flexible, allowing you to extend it for your application's needs. I think you could extend it with something like this to achieve your goals:

/**
 * @extends \Ramsey\Collection\AbstractSet<Foo>
 */
class MySet extends \Ramsey\Collection\AbstractSet
{
    public function getType(): string
    {
        return MyType::class;
    }

    /**
     * @param callable(MyType): TCallbackReturn $callback A callable to apply to each
     *     item of the collection.
     *
     * @return MySet
     *
     * @template TCallbackReturn as MyType
     */
    public function map(callable $callback): \Ramsey\Collection\CollectionInterface
    {
        return new self(array_map($callback, $this->data));
    }
}

@abenerd
Copy link
Author

abenerd commented Jan 2, 2023

I see your point about not introducing a $collection property but I found the map implementations too similar to override them, alternatively one could in the AbstractCollection create a new static collection this way you'd automatically get the correct implementation, although we would then have to take care with the constructor arguments.

I don't think this change would take away any flexibility of the library we would simply ensure the correct behavior for any implementation of a collection and it still allows users to extend any collection they want to add their own behavior.

@ramsey
Copy link
Owner

ramsey commented Jan 2, 2023

we would simply ensure the correct behavior

Is the correct behavior for the map function to always return the same type of collection?

@abenerd
Copy link
Author

abenerd commented Jan 3, 2023

I would argue so yes, when I create a new Set I expect it to behave a certain way and until I explicitly say make this Set into a Collection, or Whatever I want to be sure in the assumption that I am working with a Set.

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.

3 participants