dideod98
dideod98

Reputation: 111

how to use name of name in array

I want to use name of name in array. I searched about dictionary but my tcl version is old so I couldn't use dictionary. also I couldn't upgrade tcl version. So Could you tell me how to use name of name like below script..?

set country korea
set food kimchi
set find($country)($food) spicy

set country japan
set food susi
set find($country)($food) fresh

set country international
set food hamburger
set find($country)($food) standard

set country international
set food chicken
set find($country)($food) standard

foreach country [array names find] {
 puts $index
}
# I expect 'korea japan international'.. but It wasn't working

foreach country [array names find] {
foreach food [array names find($country)] {

 puts $country,$food,$food($country)($food)
}
}
# I expect
# korea,kimch,spicy
# japan,susi,fresh
# international,hamburger,standard
# international,chicken,standard
# but It was not working..

Upvotes: 0

Views: 55

Answers (1)

Donal Fellows
Donal Fellows

Reputation: 137627

If you can't use dictionaries, this means you're on Tcl 8.4 or before. These are not supported; the minimum supported version of Tcl is 8.5 which is currently on long-term critical-bug-fix only grade support, and 8.6 is the recommended minimum for new work. The formal advice is upgrade, even if this is impossible for you.


So, you're stuck with dictionaries? You cannot nest those (except in a few ancient versions where it was possible via a bug) so you need to find a different approach. The simplest method might be to use one array that holds the name of another array. This is probably easiest if we use a few helper procedures:

# Initialise the array ahead of time
array set find {}

proc setFoodInfo {country food info} {
    upvar 1 find find
    if {![info exists find($country)]} {
        # Generate a unique array name
        set find($country) find_[array size find]
    }
    upvar 1 $find($country) ary
    set ary($food) $info
    return
}

proc listFoods {country} {
    upvar 1 find find
    upvar 1 $find($country) ary
    return [array names ary]
}

proc getFoodInfo {country food} {
    upvar 1 find find
    upvar 1 $find($country) ary
    return $ary($food)
}

setFoodInfo korea kimchi spicy
setFoodInfo japan sushi fresh
setFoodInfo international hamburger standard
setFoodInfo international chicken standard

foreach country [array names find] {
    foreach food [listFoods $country] {
        puts $country,$food,[getFoodInfo $country $food]
    }
}

When I test it in Tcl 8.4 — I fortunately still have a copy — I get this output:

international,chicken,standard
international,hamburger,standard
japan,sushi,fresh
korea,kimchi,spicy

Tcl doesn't preserve the order of arrays, so I consider this to be acceptably similar to what you are looking for.


The actual most common technique back in old Tcl versions was to use compound keys, often with , as the compounding character:

set find(korea,kimchi) spicy
...


foreach name [array names find] {
    puts $name,$find($name)
}

but this gets quite confusing when you want to ask questions like “what countries do I have information about foods for?”. It's not insurmountable for reasonable keys, as you can use a glob pattern with array names and parse the result (e.g., with split or regexp), but it's quite messy. (For this particular example, the best answer is to use an SQL database, but that involves an extra package and possibly more external setup.)

Upvotes: 1

Related Questions