• looking for [dict equal]

    From Petro Kazmirchuk@21:1/5 to All on Wed Nov 9 02:57:07 2022
    I need to compare 2 dicts disregarding order of keys (just equal/not equal), preferably without unnecessary generation of string representations. Can't believe it's not in the core :(
    I'd prefer to import it from Tcllib or other standard package rather than copy-paste from the Wiki
    thanks in advance!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Schelte@21:1/5 to Petro Kazmirchuk on Wed Nov 9 13:14:33 2022
    On 09/11/2022 11:57, Petro Kazmirchuk wrote:
    I need to compare 2 dicts disregarding order of keys (just equal/not equal), preferably without unnecessary generation of string representations. Can't believe it's not in the core :(
    I'd prefer to import it from Tcllib or other standard package rather than copy-paste from the Wiki
    thanks in advance!

    Because Tcl is not typed, it is not possible to have a general [dict
    equal] command. Maybe some dict entries are dicts themselves. Then you
    would want those to also disregard the order of their keys. If some of
    them are lists, you may care about the order or not. Is 0x2A equal to
    42? Things like that are impossible to be inferred from only the data.

    So, as the developer of the code with knowledge of what the different
    entries may contain, you will just have to loop over one dict, check
    that it exists in the other dict and then perform the appropriate
    comparisons. Of course the first step is to compare the dict sizes.


    Schelte.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From saitology9@21:1/5 to Petro Kazmirchuk on Wed Nov 9 10:48:02 2022
    On 11/9/2022 5:57 AM, Petro Kazmirchuk wrote:
    I need to compare 2 dicts disregarding order of keys (just equal/not equal), preferably without unnecessary generation of string representations. Can't believe it's not in the core :(
    I'd prefer to import it from Tcllib or other standard package rather than copy-paste from the Wiki
    thanks in advance!


    Well, since EIAS, you can just compare them directly:

    if {$dict_1 eq $dict_2} {...}


    To disregard the ordering of the keys, you can sort them first:

    if {[lsort -dict -index 0 $dict_1] eq [lsort -dict -index 0 $dict_2]} {...}

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to Petro Kazmirchuk on Wed Nov 9 15:17:44 2022
    One may be shocked (or not really) to learn that I know nothing about
    dicts. I used to write Tcl every day and hang around the wiki and
    chat room often in the oughts, a long time ago. Then I stepped away
    from Tcl and the community for personal reasons...

    Until now.

    When I left the scene, dict was a pretty new addition and I never got
    around to learning it. I just read the man page and found it very
    confusing. I don't see how it's much different from a list. Looks like
    it's a list-like array or an array-like list. Whatever. I need to put
    some time aside to study it carefully.

    With that said, in my ignorance, I suppose you can traverse the entire
    content of a dict, can't you? It would seem useless to me if you can't.
    So it must be possible and not even very difficult to write a proc
    that will "gut" or "disassemble" a dict in multiple parts that can then
    be compared directly with the counterparts of another dict, one to one.
    A little bit like comparing two directories, each one with multiple subdirectories and files.

    Maybe you can even use md5sum to compare those "gutted" parts.

    It's an idea. But I think I've made it clear that I don't understand
    how dicts work, so pardon me if the idea is not good -- if possible
    at all.

    --
    Luc

    **************************
    On Wed, 9 Nov 2022 02:57:07 -0800 (PST), Petro Kazmirchuk wrote:

    I need to compare 2 dicts disregarding order of keys (just equal/not
    equal), preferably without unnecessary generation of string
    representations. Can't believe it's not in the core :( I'd prefer to
    import it from Tcllib or other standard package rather than copy-paste
    from the Wiki thanks in advance!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Luc on Wed Nov 9 22:14:43 2022
    Luc <no@no.no> wrote:
    When I left the scene, dict was a pretty new addition and I never got
    around to learning it. I just read the man page and found it very
    confusing. I don't see how it's much different from a list. Looks like
    it's a list-like array or an array-like list. Whatever. I need to put
    some time aside to study it carefully.

    One way of viewing it is as an array with "ordering" -- the ordering
    being the insertion order of the keys.

    The big difference between dicts and array's is that arrays as they
    were built way back in the beginning of Tcl, are logically collections
    of variables (each array entry is as if it was a separate variable
    underneath).

    That is why you can't use $array to get all of the contents of an
    array, and why you can't use $array to pass an entire array into a proc
    as a parameter. You have to pass the name of the array, and then use
    upvar to link the passed in name to a variable within the proc.

    Dicts are arrays that do not have that restriction of being
    "collections of variables". Because of that, one can do "puts $dict" to
    obtain the string representation of the dict on an output channel, and
    one can use $dict to pass a dict to a proc as a parameter, instead of
    having to pass in the name of the dict.

    Now, one could argue that the 'definition' of arrays could have also
    changed to allow $array to retreive the string rep., and to allow
    passing entire arrays into a proc via $array syntax. My *guess* as to
    why that was not done is doing so was likely seen as to much of a risk
    of a breaking change to very old Tcl syntax, so the definiton of arrays
    was unchanged and [dict] was added.

    With that said, in my ignorance, I suppose you can traverse the entire content of a dict, can't you? It would seem useless to me if you can't.

    You can.

    So it must be possible and not even very difficult to write a proc
    that will "gut" or "disassemble" a dict in multiple parts that can then
    be compared directly with the counterparts of another dict, one to one.

    For your own dicts that you create in your own programs, yes, this is
    quite possible. Because you know what each leg of the tree is supposed
    to be.

    But creating a generic "equal" procedure, that operates on any two
    arbitrary dicts from any coder's piece of code, is what is not
    possible. Because any value could itself be another dict, or a plain
    list, or just a string. But because Tcl is typeless, there is no way
    for a generic Tcl "dict equals" to know, when encountering "this might
    be a dict here" whether that is just a string of characters, a list of
    six words, or a dict with three keys and three values. More often than
    not, the distinction won't matter, but there will be enough instances
    where the distinction does matter than a generic 'dict equals' can't be
    created that works for all possible inputs.

    A little bit like comparing two directories, each one with multiple subdirectories and files.

    Except, imagine that you can't know when you encounter something at
    level 3, whether it is a file or a directory. If you don't know which
    are directories, you don't know which ones to further descend into.
    That's the problem with a generic 'dict equals' intended to work on any possible input.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Petro Kazmirchuk@21:1/5 to All on Thu Nov 10 00:50:20 2022
    Thank you all for responses. Now I understand why there's no generic [dict equal]
    yet another thing to reinvent in my code :-( alongside assert, sleep, vwait with timeout and endless argument parsing

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ralf Fassel@21:1/5 to All on Thu Nov 10 11:58:54 2022
    * Rich <rich@example.invalid>
    | Luc <no@no.no> wrote:
    | > A little bit like comparing two directories, each one with multiple
    | > subdirectories and files.

    | Except, imagine that you can't know when you encounter something at
    | level 3, whether it is a file or a directory. If you don't know which
    | are directories, you don't know which ones to further descend into.
    | That's the problem with a generic 'dict equals' intended to work on any
    | possible input.

    For example:

    % set a [dict create val1 [list val11 val12]]
    val1 {val11 val12}

    % set b [dict create val1 [dict create val11 val12]]
    val1 {val11 val12}

    % set c [dict create val1 "val12 val12"]
    val1 {val11 val12}

    'a' has a *list* as value for the key, 'b' has a *dict* as value for the
    same key, 'c' a *string*. They all have the same string representation,
    but are they *equal*?

    For files and directories you have [file isdirectory] and [file isfile],
    but that info is not easily available for the dict elements (and
    probably not even reliably, considering "dict set a val1 val11 val13" is possible).

    R'

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Petro Kazmirchuk on Thu Nov 10 12:59:38 2022
    Petro Kazmirchuk <petro.kazmirchuk@gmail.com> wrote:
    Thank you all for responses. Now I understand why there's no generic
    [dict equal]

    yet another thing to reinvent in my code :-( alongside assert, sleep,
    vwait with timeout and endless argument parsing

    For 'asssert' there is the 'error' command and/or 'return -code error'.

    For argument parsing, one option is to use 'cmdline' from Tcllib (if
    you do not yet have Tcllib installed, then I recommend you do so, it
    contains a lot of ready made pieces).

    For a blocking sleep, plain 'after' works just fine. But if you
    instead want a sleep that does not otherwise block the event loop then
    yes there is a little work there.

    vwait with timeout is just vwait with a background after to 'unwait'
    the vwait if the timeout expires. Better done as a namespace ensemble
    or an actual object just to keep the necessary variables isolated.

    You will want to be careful of the 'nested vwait' issue:

    https://wiki.tcl-lang.org/page/vwait

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Oleg Nemanov@21:1/5 to All on Thu Nov 10 05:27:02 2022
    среда, 9 ноября 2022 г. в 13:57:10 UTC+3, Petro Kazmirchuk:
    I need to compare 2 dicts disregarding order of keys (just equal/not equal), preferably without unnecessary generation of string representations. Can't believe it's not in the core :(
    I'd prefer to import it from Tcllib or other standard package rather than copy-paste from the Wiki
    thanks in advance!

    You can't compare 2 dicts without additional info from the side about every value type(is this a leaf element - string, for example; or this is a dict).
    If you create a dict with "dict create" and "dict set" commands, then you can try to derive a type info with help of ::tcl::unsupported::representation.

    It would be better if tcl has no shimmering, but instead has explicit routines to stringify and parse values, imho.
    Implicit data type convertion(shimmering) is the cause of hardly findable bugs.

    May be, sometime in the future we will get something like tcl strict mode(turnable by some option) for turning off shimmering.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Luc@21:1/5 to Ralf Fassel on Thu Nov 10 12:49:24 2022
    On Thu, 10 Nov 2022 11:58:54 +0100, Ralf Fassel wrote:

    For example:

    % set a [dict create val1 [list val11 val12]]
    val1 {val11 val12}

    % set b [dict create val1 [dict create val11 val12]]
    val1 {val11 val12}

    % set c [dict create val1 "val12 val12"]
    val1 {val11 val12}

    'a' has a *list* as value for the key, 'b' has a *dict* as value for the
    same key, 'c' a *string*. They all have the same string representation,
    but are they *equal*?

    For files and directories you have [file isdirectory] and [file isfile],
    but that info is not easily available for the dict elements (and
    probably not even reliably, considering "dict set a val1 val11 val13" is possible).

    R'


    For the strict sake of comparison, I would treat all occurrences of
    [llength $list] <= 1 as strings.

    Or maybe treat all non-space strings as [list $string].

    About the dicts, I don't know. I still don't know how they really work
    until I reserve some time to study them adequately.

    Of course, there is a 99.9% probability that you are right and I am wrong.

    I'm just very stubborn. It can be a valuable trait in certain situations.

    --
    Luc


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Harald Oehlmann@21:1/5 to All on Thu Nov 10 17:56:09 2022
    Am 10.11.2022 um 17:39 schrieb Ralf Fassel:
    * Luc <no@no.no>
    | About the dicts, I don't know. I still don't know how they really work
    | until I reserve some time to study them adequately.

    Always a good idea :-) I don't know the internals of dicts either, but
    Rich has listed the advantages when using them instead of arrays up-thread.

    | I'm just very stubborn. It can be a valuable trait in certain situations.

    I wouldn't call that stubborn in this context. If you're honestly
    trying to understand what this is all about, that's a Good Thing¹ IMHO.

    R'
    ---
    ¹ http://www.catb.org/~esr/jargon/html/G/Good-Thing.html

    To guess the type of a list value, you may look what the pdict does with values:

    https://wiki.tcl-lang.org/page/pdict%3A+Pretty+print+a+dict?R=0&O=pdict&W=

    Look at the line:
    [string match "value is a dict*"\
    [tcl::unsupported::representation $val]]

    Enjoy,
    Harald

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ralf Fassel@21:1/5 to All on Thu Nov 10 17:39:35 2022
    * Luc <no@no.no>
    | About the dicts, I don't know. I still don't know how they really work
    | until I reserve some time to study them adequately.

    Always a good idea :-) I don't know the internals of dicts either, but
    Rich has listed the advantages when using them instead of arrays up-thread.

    | I'm just very stubborn. It can be a valuable trait in certain situations.

    I wouldn't call that stubborn in this context. If you're honestly
    trying to understand what this is all about, that's a Good Thing¹ IMHO.

    R'
    ---
    ¹ http://www.catb.org/~esr/jargon/html/G/Good-Thing.html

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Luc on Thu Nov 10 18:14:13 2022
    Luc <no@no.no> wrote:
    On Thu, 10 Nov 2022 11:58:54 +0100, Ralf Fassel wrote:

    For example:

    % set a [dict create val1 [list val11 val12]]
    val1 {val11 val12}

    % set b [dict create val1 [dict create val11 val12]]
    val1 {val11 val12}

    % set c [dict create val1 "val12 val12"]
    val1 {val11 val12}

    'a' has a *list* as value for the key, 'b' has a *dict* as value for the
    same key, 'c' a *string*. They all have the same string representation,
    but are they *equal*?

    For files and directories you have [file isdirectory] and [file isfile],
    but that info is not easily available for the dict elements (and
    probably not even reliably, considering "dict set a val1 val11 val13" is
    possible).

    R'


    For the strict sake of comparison, I would treat all occurrences of
    [llength $list] <= 1 as strings.

    Or maybe treat all non-space strings as [list $string].

    Whereupon you delve into the philosophical discussion of what it means to
    be "equal" and the reason Javascript has both == and === operators for different flavors of equality.

    Is any "thing" (list, dict string) "equal" if its string representation
    is equal? Some will say yes, some will say no.

    For those that say no, they will want equal to mean: if the thing is a
    list, then each of its individual elements are "equal" (to the same
    recursive definition they use for "equal"). But those same folks would
    say that a list of four elements is not equal to a dict of two keys and
    two values, even if the "Tcl string representation" of both is
    identical.

    I.e.:

    $ rlwrap tclsh
    % set a [list one two three four]
    one two three four
    % set b [dict create one two three four]
    one two three four
    % string equal $a $b
    1

    $a is a list of four elements. $b is a dict of two key/value pairs.
    Their string representations are equal, but a list is a subtly
    different 'thing' than a dict. So some would say that $a should not be
    equal to $b because they are different "types" underneath. Those that
    usualy want such strict definitions also often use languages who's type
    systems enforce such definitions, where a variable defined as a "list"
    is never equal to a variable defined as a "dict" because list and dict
    are two different "types" in the language.

    One single 'equal' can't satisify both sides of that divide.

    About the dicts, I don't know. I still don't know how they really work
    until I reserve some time to study them adequately.

    At a very broad level, dict's are essentially just arrays that you can
    pass into procs by value instead of by name only.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rich@21:1/5 to Harald Oehlmann on Thu Nov 10 18:21:12 2022
    Harald Oehlmann <wortkarg3@yahoo.com> wrote:
    Am 10.11.2022 um 17:39 schrieb Ralf Fassel:
    * Luc <no@no.no>
    | About the dicts, I don't know. I still don't know how they really
    work | until I reserve some time to study them adequately.

    Always a good idea :-) I don't know the internals of dicts either,
    but Rich has listed the advantages when using them instead of arrays
    up-thread.

    | I'm just very stubborn. It can be a valuable trait in certain
    situations.

    I wouldn't call that stubborn in this context. If you're honestly
    trying to understand what this is all about, that's a Good Thing
    IMHO.

    R'
    ---
    http://www.catb.org/~esr/jargon/html/G/Good-Thing.html

    To guess the type of a list value, you may look what the pdict does
    with values:

    https://wiki.tcl-lang.org/page/pdict%3A+Pretty+print+a+dict?R=0&O=pdict&W=

    Look at the line:
    [string match "value is a dict*"\
    [tcl::unsupported::representation $val]]

    True, but that big "unsupported" there in the proc namespace should
    serve as a warning....

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