• Re: set.add() doesn't replace equal element

    From Chris Angelico@21:1/5 to Ian Pilcher on Sat Dec 31 08:58:20 2022
    On Sat, 31 Dec 2022 at 08:42, Ian Pilcher <[email protected]> wrote:

    I just discovered this behavior, which is problematic for my particular
    use. Is there a different set API (or operator) that can be used to
    add an element to a set, and replace any equal element?

    If not, am I correct that I should call set.discard() before calling set.add() to achieve the behavior that I want?


    Use a dictionary. Initially, map everything to itself. You can then
    replace things with the new keys:

    stuff = {}
    stuff[1] = 1
    stuff[4] = 4
    stuff[True] = True
    stuff[2] = 2
    stuff[4.0] = 4.0
    stuff
    {1: True, 4: 4.0, 2: 2}

    To see what's in your set, look at the dictionary's values. They will
    replace any equal elements, leaving the keys unchanged.

    ChrisA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Stefan Ram@21:1/5 to Ian Pilcher on Fri Dec 30 21:54:37 2022
    Ian Pilcher <[email protected]> writes:
    If not, am I correct that I should call set.discard() before calling >set.add() to achieve the behavior that I want?

    From the POV of a set, there are no "equal elements", but two
    equal objects are just the same thing; so it makes no sense
    to replace one by the other. You seem to want not a set but
    something else. If you can get that behavior by using "set.
    discard", you can use it. But that dissonance might be an early
    warning that more problems with set might be waiting down
    your road.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ian Pilcher@21:1/5 to All on Fri Dec 30 15:41:19 2022
    I just discovered this behavior, which is problematic for my particular
    use. Is there a different set API (or operator) that can be used to
    add an element to a set, and replace any equal element?

    If not, am I correct that I should call set.discard() before calling
    set.add() to achieve the behavior that I want?

    --
    ========================================================================
    Google Where SkyNet meets Idiocracy ========================================================================

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Bryan@21:1/5 to [email protected] on Fri Dec 30 13:47:59 2022
    What kind of elements are being added to the set? Can you show
    reproducible sample code?

    On Fri, Dec 30 2022 at 03:41:19 PM -0600, Ian Pilcher
    <[email protected]> wrote:
    I just discovered this behavior, which is problematic for my
    particular
    use. Is there a different set API (or operator) that can be used to
    add an element to a set, and replace any equal element?

    If not, am I correct that I should call set.discard() before calling set.add() to achieve the behavior that I want?

    --
    ======================================================================== Google Where SkyNet meets
    Idiocracy ========================================================================
    --
    <https://mail.python.org/mailman/listinfo/python-list>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From [email protected]@21:1/5 to All on Fri Dec 30 17:03:54 2022
    Ian,

    Do you have some examples of things you can put in a set that you consider equal but want to store in the set INSTEAD of any current element?

    What follows is some thoughts on some methods you could build yourself.
    Others re possible and someone else may present you with a module that does what you want.

    In principle, if you had a function that produced a canonical version of an argument as a sort of FIRST choice, you might still have to do some serious work.

    Assuming alpha, beta and gamma are all what you define as equal, such as if
    you consider the value of a string like "32.00" to be equal to
    "32.000000000" or even to "64/2", then you can get ALL elements of the set
    and call your function on it and compare it to the function applied to your new proposed set addition. So f(alpha)and f(beta) and f(gamma) would by definition return the same result, albeit that might be an integer like 32,
    or a string of "32.0" or whatever you need.

    Now are you sure the set has no "duplicates" already per your algorithm?

    If you are, your algorithm would need to loop on all keys and if a key
    matches using f() and the newer version is not otherwise identical, remove
    the old and insert the new. You can break out of the loop at that point BUT
    if there is no uniqueness guarantee, you need to process all keys every
    time.

    A possible improvement would be to consider various mixed approaches. You
    could use a dictionary where the keys are f(whatever) and the values are the current "whatever" as one example. The "set" then would be the values of the dictionary when you need that view, and the dictionary as a whole when you want to add or delete. If you needed operations like union and intersect, it may take more work.

    And, of course, you can maintain two sets. The other set would contain only f(whatever) while the first would contain the "whatever"s.

    What you are describing may have been implemented with a concept like partitions. An example might be the partitioning of numbers modulo N so that numbers are considered "equal" if they leave the same remainder when divided
    by N. Rather than use a set directly, this could be better done by creating
    an object that manages internal data structures so asking it to add a new number results in it calculating the remainder, seeing if it is in use, and replacing it with the latest if needed.

    Avi


    -----Original Message-----
    From: Python-list <python-list-bounces+avi.e.gross=[email protected]> On Behalf Of Ian Pilcher
    Sent: Friday, December 30, 2022 4:41 PM
    To: [email protected]
    Subject: set.add() doesn't replace equal element

    I just discovered this behavior, which is problematic for my particular use.
    Is there a different set API (or operator) that can be used to add an
    element to a set, and replace any equal element?

    If not, am I correct that I should call set.discard() before calling
    set.add() to achieve the behavior that I want?

    --
    ========================================================================
    Google Where SkyNet meets Idiocracy ========================================================================
    --
    https://mail.python.org/mailman/listinfo/python-list

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul Bryan@21:1/5 to [email protected] on Fri Dec 30 15:00:15 2022
    It seems to me like you have to ideas of what "equal" means. You want
    to update a "non-equal/equal" value in the set (because of a different
    time stamp). If you truly considered them equal, the time stamp would
    be irrelevant and updating the value in the set would be unnecessary.

    I would:

    a) /not/ consider two different leases with two different time stamps
    to be equal, and
    b) as already mentioned, store them in another data structure like a dictionary.

    Not knowing the specifics of the DHCP object structure, if a DHCP lease
    object has some immutable key or other durable immutable attribute, I
    would be inclined to make that the dictionary key, and store the DHCP
    object as the value.


    On Fri, Dec 30 2022 at 04:27:56 PM -0600, Ian Pilcher
    <[email protected]> wrote:
    On 12/30/22 15:47, Paul Bryan wrote:
    What kind of elements are being added to the set? Can you show
    reproducible sample code?

    The objects in question are DHCP leases. I consider them "equal" if
    the lease address (or IPv6 prefix) is equal, even if the timestamps
    have
    changed. That code is not small, but it's easy to demonstrate the
    behavior.

    import datetime
    class Foo(object):
    ... def __init__(self, index):
    ... self.index = index
    ... self.timestamp = datetime.datetime.now()
    ... def __eq__(self, other):
    ... return type(other) is Foo and other.index == self.index
    ... def __hash__(self):
    ... return hash(self.index)
    ... def __repr__(self):
    ... return f'Foo({self.index}) created at
    {str(self.timestamp)}'
    ...
    f1 = Foo(1)
    s = { f1 }
    s
    {Foo(1) created at 2022-12-30 16:24:12.352908}
    f2 = Foo(1)
    f2
    Foo(1) created at 2022-12-30 16:24:35.489208
    s.add(f2)
    s
    {Foo(1) created at 2022-12-30 16:24:12.352908}

    --
    ======================================================================== Google Where SkyNet meets
    Idiocracy ========================================================================


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Chris Angelico@21:1/5 to Ian Pilcher on Sat Dec 31 09:46:45 2022
    On Sat, 31 Dec 2022 at 09:29, Ian Pilcher <[email protected]> wrote:

    On 12/30/22 15:47, Paul Bryan wrote:
    What kind of elements are being added to the set? Can you show
    reproducible sample code?

    The objects in question are DHCP leases. I consider them "equal" if
    the lease address (or IPv6 prefix) is equal, even if the timestamps have changed. That code is not small, but it's easy to demonstrate the
    behavior.

    Seems the more logical approach would be to use a dictionary mapping
    the lease address/prefix to the timestamp? The part that contributes
    to the equality check is the key, and everything else is the value.

    ChrisA

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ian Pilcher@21:1/5 to Paul Bryan on Fri Dec 30 16:27:56 2022
    On 12/30/22 15:47, Paul Bryan wrote:
    What kind of elements are being added to the set? Can you show
    reproducible sample code?

    The objects in question are DHCP leases. I consider them "equal" if
    the lease address (or IPv6 prefix) is equal, even if the timestamps have changed. That code is not small, but it's easy to demonstrate the
    behavior.

    import datetime
    class Foo(object):
    ... def __init__(self, index):
    ... self.index = index
    ... self.timestamp = datetime.datetime.now()
    ... def __eq__(self, other):
    ... return type(other) is Foo and other.index == self.index
    ... def __hash__(self):
    ... return hash(self.index)
    ... def __repr__(self):
    ... return f'Foo({self.index}) created at {str(self.timestamp)}'
    ...
    f1 = Foo(1)
    s = { f1 }
    s
    {Foo(1) created at 2022-12-30 16:24:12.352908}
    f2 = Foo(1)
    f2
    Foo(1) created at 2022-12-30 16:24:35.489208
    s.add(f2)
    s
    {Foo(1) created at 2022-12-30 16:24:12.352908}

    --
    ========================================================================
    Google Where SkyNet meets Idiocracy ========================================================================

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ian Pilcher@21:1/5 to Paul Bryan on Sat Dec 31 10:41:32 2022
    On 12/30/22 17:00, Paul Bryan wrote:
    It seems to me like you have to ideas of what "equal" means. You want to update a "non-equal/equal" value in the set (because of a different time stamp). If you truly considered them equal, the time stamp would be irrelevant and updating the value in the set would be unnecessary.

    I would:

    a) /not/ consider two different leases with two different time stamps to
    be equal, and
    b) as already mentioned, store them in another data structure like a dictionary.

    Not knowing the specifics of the DHCP object structure, if a DHCP lease object has some immutable key or other durable immutable attribute, I
    would be inclined to make that the dictionary key, and store the DHCP
    object as the value.

    I have come to the conclusion that you are correct. Thanks!

    --
    ========================================================================
    Google Where SkyNet meets Idiocracy ========================================================================

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)