markt1964
markt1964

Reputation: 2836

Trying to force an entry in an array to be an array

I am trying to create an associative array of associative arrays in gawk, and what I initially tried was:

options[key][subkey] = 1

However, when it got to this line, I unceremoniously received the error fatal: attempt to use scalar 'option["Declaration"]' as an array ("Declaration" being one of the main keys that my program uses, although I presume the exact value is irrelevant. At this particular point in the program, there was no "Declaration" entry assigned, although there were entries which had "Declaration" as a subkey on other entries, which may be meaningful).

So with a bit of googling, I found another stackoverflow question that looked like it answered my issue, so I put the following code immediately above it:

if (typeof(options[key])!="array") {
    options[key] = 0;
    delete options[key];
    split("",options[key]);
}

However, this does not work either, instead now giving me the error: fatal: split: second argument is not an array

What am I doing wrong?

EDIT: Note, that I cannot use a basic 2-dimensional array here... for what I am doing, it is important that I am using one associative array to another because I need to be able to later identify the subkeys that were used on a given key.

Pursuant to requests below, I am posting the relevant functions that use the associative array, which may help clarify what is going on.

function add_concrete(key, concrete) {
    if (key == concrete) {
        return;
    }
    if (length(options[key])>0) {
        for(i in options[key]) {
            add_concrete(i, concrete);
        }
    }
    contains[key][concrete] = 1
}

function add_options(name, value) {
    subkey = trim(name);
    if (subkey == "") {
        return;
    }
    if (match(value, ";") > 0) {
        exporting = 0;
    }
    split(value, args, /[ |;]*/);
    for (i in args) {
        key = trim(args[i]);
        if (key != "") {
            print("Adding " name " to " key);
            options[key][subkey] = 1
            if (concrete[key]) {
                add_concrete(subkey, key);
            }
        }
    }
}

Upvotes: 0

Views: 130

Answers (2)

RARE Kpop Manifesto
RARE Kpop Manifesto

Reputation: 2875

Don't use delete …… it's so brute force

Use a dummy variable for a soft init :

for (_ in options[key]) {

    break
}

This way you can guarantee options[key] is now a sub-array, while preserving any pre-existing values it might already have.

With this approach, if you're very certain options[key] could only be a sub-array, then you can skip all the checks for typeof( ) or isarray( ).

The instant break also ensures it spends practically zero amount of time in the dummy iterator.

If you need a unified function to calculate array length() that's portable while leveraging built-in length(arr) of select awk variants, here's what I have in my own library :

function lengthA(__, _, ___) {

    for (_ in __)

        if (_ = "\x6" < "x\6")   # gawk --posix (-P) mode detector
            return length(__)
        else
            break

    for (___ in __)
            _++
    return +_
}

lengthA( ) is a fully POSIX-compliant awk function that has no semi colons or braces anywhere (other than the mandatory pair enclosing the function). awk's syntax is surprisingly modern looking for a 47-year old language.

The utter lack of types in awk is a godsend. Just within the same function, the temp variable _ performed 3 different roles without the need to explicitly recast during any transition phases :

  • Index for current element of iterator
  • Boolean outcome of string compare, which also serves to initialize accumulator to zero
  • Accumulator for count of array indices

Upvotes: 0

James Brown
James Brown

Reputation: 37424

Sorry, cooking at the same time. As you didn't post much, don't have much to work with, but with no "initialization":

$ awk 'BEGIN {
    options[key] = 0;
    delete options[key];
#    options[key][1]          # cant see me
    split("",options[key]);
}'
awk: cmd. line:5: fatal: split: second argument is not an array

But with "initialization":

$ awk 'BEGIN {
    options[key] = 0;
    delete options[key];
    options[key][1]          # can see me
    split("",options[key]);
}'
$_ # see this cursor happily blinking without any error

Upvotes: 2

Related Questions