awk sort многомерный массив

GNU awk поддерживает многомерные массивы:

q[1][1] = "dog"
q[1][2] = 999
q[2][1] = "mouse"
q[2][2] = 777
q[3][1] = "bird"
q[3][2] = 888

Я хотел бы отсортировать «второй столбец» q так, чтобы у меня осталось:

q[1][1] = "mouse"
q[1][2] = 777
q[2][1] = "bird"
q[2][2] = 888
q[3][1] = "dog"
q[3][2] = 999

как вы можете видеть, значения «первого столбца» перемещены, чтобы сохранить второй. Я вижу, что GNU Awk предлагает функцию asort, но она не поддерживает многомерные массивы. Если это поможет, вот рабочий пример Ruby:

q = [["dog", 999], ["mouse", 777], ["bird", 888]]
q.sort_by{|z|z[1]}
=> [["mouse", 777], ["bird", 888], ["dog", 999]]

В итоге я использовал обычный массив, а затем разделил дубликаты символами новой строки:

q[777] = "mouse"
q[999] = "dog" RS "fish"
q[888] = "bird"
for (z in q) {
  print q[z]
}

person Steven Penny    schedule 17.07.2013    source источник
comment
Насколько мне известно, язык не предоставляет встроенный метод для сортировки массивов multi-D по желанию пользователя. (дайте мне знать, если какой-то язык поддерживает) (g) awk либо. Так что вы должны запачкать руки и написать свою собственную логику. как вы сказали, gawk предоставляет asort() последний аргумент может быть определяемой пользователем функцией сравнения. Вы можете написать свою собственную логику. кроме того, вы можете превратить двумерный массив в одномерный массив и использовать встроенную функцию asort(). что ты уже испробовал?   -  person Kent    schedule 17.07.2013
comment
Первоначальный вопрос, похоже, касался сортировки многомерных массивов, и на него был принят ответ (stackoverflow.com/a/17706399/1745001) более 3 лет, но ОП недавно не принял этот ответ, опубликовал свой собственный ответ, который был просто о заполнении одномерного массива, а затем обновил свой вопрос, чтобы сказать, что это то, что они действительно хотели, так что теперь это просто дубликат каждого другой вопрос о заполнении ассоциативного массива, и его следует просто закрыть, поскольку он больше не имеет смысла отдельно и, конечно, не в отношении многомерных массивов.   -  person Ed Morton    schedule 05.01.2017


Ответы (2)


FWIW, вот обходной путь функции sort_by():

$ cat tst.awk
BEGIN {
    a[1][1] = "dog"
    a[1][2] = 999
    a[2][1] = "mouse"
    a[2][2] = 777
    a[3][1] = "bird"
    a[3][2] = 888

    print "\n############################\nBefore:"
    for (i=1; i in a; i++)
        for (j=1; j in a[i]; j++)
            printf "a[%d][%d] = %s\n",i,j,a[i][j]
    print "############################"

    sort_by(a,2)

    print "\n############################\nAfter:"
    for (i=1; i in a; i++)
        for (j=1; j in a[i]; j++)
            printf "a[%d][%d] = %s\n",i,j,a[i][j]
    print "############################"

}

function sort_by(arr,key,       keys,vals,i,j)
{
    for (i=1; i in arr; i++) {
        keys[i] = arr[i][key]
        for (j=1; j in arr[i]; j++)
            vals[keys[i]] = vals[keys[i]] (j==1?"":SUBSEP) arr[i][j]
    }

    asort(keys)

    for (i=1; i in keys; i++)
       split(vals[keys[i]],arr[i],SUBSEP)

    return (i - 1)
}

$ gawk -f tst.awk

############################
Before:
a[1][1] = dog
a[1][2] = 999
a[2][1] = mouse
a[2][2] = 777
a[3][1] = bird
a[3][2] = 888
############################

############################
After:
a[1][1] = mouse
a[1][2] = 777
a[2][1] = bird
a[2][2] = 888
a[3][1] = dog
a[3][2] = 999
############################

Он работает, сначала конвертируя это:

    a[1][1] = "dog"
    a[1][2] = 999
    a[2][1] = "mouse"
    a[2][2] = 777
    a[3][1] = "bird"
    a[3][2] = 888

к этому:

    keys[1]   = 999
    vals[999] = dog SUBSEP 999

    keys[2]   = 777
    vals[777] = mouse SUBSEP 777

    keys[3]   = 888
    vals[888] = bird SUBSEP 888

затем asort()ing keys[] чтобы получить:

    keys[1] = 777
    keys[2] = 888
    keys[3] = 999

а затем перебирать массив ключей, используя его элементы в качестве индексов для массива vals для повторного заполнения исходного массива.

Если кому-то интересно, почему я просто не использовал значения, которые мы хотим отсортировать, в качестве индексов, а затем не выполнил asorti(), поскольку это привело бы к немного более короткому коду, вот почему:

$ cat tst.awk
BEGIN {
   a[1] = 888
   a[2] = 9
   a[3] = 777

   b[888]
   b[9]
   b[777]

   print "\n\"a[]\" sorted by content:"
   asort(a,A)
   for (i=1; i in A; i++)
      print "\t" A[i]

   print "\n\"b[]\" sorted by index:"
   asorti(b,B)
   for (i=1; i in B; i++)
      print "\t" B[i]

}
$ awk -f tst.awk

"a[]" sorted by content:
        9
        777
        888

"b[]" sorted by index:
        777
        888
        9

Обратите внимание, что asorti() считает "9" более высоким значением, чем "888". Это связано с тем, что asorti() сортирует по индексам массива, а все индексы массива являются строками (даже если они выглядят как числа), и в алфавитном порядке первый символ строки «9» выше, чем первый символ строки «888». asort(), с другой стороны, сортирует содержимое массива, а содержимое массива может быть строкой ИЛИ числами, и поэтому применяются обычные правила сравнения awk — все, что выглядит как число, обрабатывается как число, а число 9 меньше, чем число. число 888, что в данном случае ИМХО является желаемым результатом.

person Ed Morton    schedule 17.07.2013

поддерживает настоящие многомерные массивы

Нет, это не так. Он поддерживает массивы массивов и поддерживает хэш, индексированный строкой, состоящей из двух сложенных вместе индексов. Ваш синтаксис прежний (массивы массивов).

Тем не менее, я не думаю, что вы можете сделать это со встроенными функциями, поскольку для этого потребуется либо использование обратного вызова компаратора, либо, альтернативно, возможность вернуть перестановку сортировки, ни одна из которых gawk не предоставляет, AFAIK.

Но вы можете обратиться к этой странице, где описано, как реализовать qsort для себя, где вы можете изменить сравнение с A[i] < A[left] до A[i][2] < A[left][2].

person Amadan    schedule 17.07.2013