Сортировать ассоциативный массив с помощью AWK - PullRequest
12 голосов
/ 17 марта 2011

Вот мой массив (скрипт gawk):

myArray["peter"] = 32
myArray["bob"] = 5
myArray["john"] = 463
myArray["jack"] = 11

После сортировки мне нужен следующий результат:

bob    5
jack   11
peter  32
john   463

Когда я использую "asort", индексы теряются. Как отсортировать по значению массива без потери индексов? (Мне нужны упорядоченные индексы на основе их значений)

(мне нужно получить этот результат только с помощью awk / gawk, а не сценария оболочки, perl и т. Д.)

Если мой пост недостаточно ясен, вот другой пост, объясняющий ту же проблему: http://www.experts -exchange.com / Программирование / Языки / Сценарии / Shell / Q_26626841.html )

Заранее спасибо

Обновление:

Спасибо вам обоим, но мне нужно сортировать по значениям, а не по индексам (я хочу упорядоченные индексы по их значениям).

Другими словами, мне нужен этот результат:

bob    5
jack   11
peter  32
john   463

не:

bob 5
jack 11
john 463
peter 32

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

Из кода Catcall я написал быструю реализацию, которая работает, но она довольно уродлива (я объединяю ключи и значения перед сортировкой и разделением во время сравнения). Вот как это выглядит:

function qsort(A, left, right,   i, last) {
  if (left >= right)
    return
  swap(A, left, left+int((right-left+1)*rand()))
  last = left
  for (i = left+1; i <= right; i++)
    if (getPart(A[i], "value") < getPart(A[left], "value"))
      swap(A, ++last, i)
  swap(A, left, last)
  qsort(A, left, last-1)
  qsort(A, last+1, right)
}

function swap(A, i, j,   t) {
  t = A[i]; A[i] = A[j]; A[j] = t
}

function getPart(str, part) {
  if (part == "key")
    return substr(str, 1, index(str, "#")-1)
  if (part == "value")
    return substr(str, index(str, "#")+1, length(str))+0
  return
}

BEGIN {  }
      {  }
END {

  myArray["peter"] = 32
  myArray["bob"] = 5
  myArray["john"] = 463
  myArray["jack"] = 11

  for (key in myArray)
    sortvalues[j++] = key "#" myArray[key]

  qsort(sortvalues, 0, length(myArray));

  for (i = 1; i <= length(myArray); i++)
    print getPart(sortvalues[i], "key"), getPart(sortvalues[i], "value")
}

Конечно, мне интересно, есть ли у вас что-нибудь более чистое ...

Спасибо за ваше время

Ответы [ 5 ]

23 голосов
/ 17 марта 2011

Изменить:

Сортировка по значениям

Oh! Чтобы отсортировать значения , это немного круто, но вы можете создать временный массив, используя объединение значений и индексов исходного массива в качестве индексов в новом массиве. Затем вы можете asorti() временный массив и разделить объединенные значения обратно на индексы и значения. Если вы не можете следовать этому запутанному описанию, код будет намного легче понять. Это также очень коротко.

# right justify the integers into space-padded strings and cat the index
# to create the new index
for (i in myArray) tmpidx[sprintf("%12s", myArray[i]),i] = i
num = asorti(tmpidx)
j = 0
for (i=1; i<=num; i++) {
    split(tmpidx[i], tmp, SUBSEP)
    indices[++j] = tmp[2]  # tmp[2] is the name
}
for (i=1; i<=num; i++) print indices[i], myArray[indices[i]]

Редактировать 2:

Если у вас GAWK 4, вы можете проходить массив по порядку значений, не выполняя явную сортировку:

#!/usr/bin/awk -f
BEGIN {
    myArray["peter"] = 32
    myArray["bob"] = 5
    myArray["john"] = 463
    myArray["jack"] = 11

    PROCINFO["sorted_in"] = "@val_num_asc"

    for (i in myArray) {
        {print i, myArray[i]}}
    }

 }

Существуют настройки для перемещения по индексу или значению, по возрастанию или по убыванию и другие параметры. Вы также можете указать пользовательскую функцию.

Предыдущий ответ:

Сортировка по показателям

Если у вас есть AWK, например gawk 3.1.2 или выше, который поддерживает asorti():

#!/usr/bin/awk -f
BEGIN {
    myArray["peter"] = 32
    myArray["bob"] = 5
    myArray["john"] = 463
    myArray["jack"] = 11

    num = asorti(myArray, indices)
    for (i=1; i<=num; i++) print indices[i], myArray[indices[i]]
}

Если у вас нет asorti():

#!/usr/bin/awk -f
BEGIN {
    myArray["peter"] = 32
    myArray["bob"] = 5
    myArray["john"] = 463
    myArray["jack"] = 11

    for (i in myArray) indices[++j] = i
    num = asort(indices)
    for (i=1; i<=num; i++) print i, indices[i], myArray[indices[i]]
}
5 голосов
/ 08 января 2013

Используйте команду сортировки Unix с конвейером, сохраняйте код Awk простым и следуйте философии Unix
Создайте входной файл со значениями, разделенными запятой
peter, 32
jack, 11
john, 463
bob, 5

Создайте файл sort.awk с кодом

BEGIN { FS=","; }
{
    myArray[$1]=$2;
}
END {
    for (name in myArray)
        printf ("%s,%d\n", name, myArray[name]) | "sort -t, -k2 -n"
}

Запустите программу, вы получите выходные данные
$ awk -f sort.awk data
Боб, 5
Джек, 11
Питер, 32
Джон, 463

3 голосов
/ 12 марта 2015
PROCINFO["sorted_in"] = "@val_num_desc";

Перед итерацией массива используйте приведенный выше оператор. Но это работает в awk версии 4.0.1. Это не работает в awk версии 3.1.7.

Я не уверен, в какой промежуточной версии он появился.

1 голос
/ 02 октября 2016

И простой ответ ...

function sort_by_myArray(i1, v1, i2, v2) {
    return myArray[i2] < myArray[i1];
}

BEGIN {
    myArray["peter"] = 32;
    myArray["bob"] = 5;
    myArray["john"] = 463;
    myArray["jack"] = 11;
    len = length(myArray);

    asorti(myArray, k, "sort_by_myArray");

    # Print result.
    for(n = 1; n <= len; ++n) {
            print k[n], myArray[k[n]]
    }
}
0 голосов
/ 17 марта 2011

Авторы Awk Programming Language предоставляют функцию быстрой сортировки , которая доступна онлайн.

Я думаю, ты бы сделал что-то подобное.

END {
  for (key in myArray) {
    sortkeys[j++] = key;
  }
  qsort(sortkeys, 0, length(myArray));      # Not sure I got the args right.
  for (i = 1; i <= length(myArray); i++) {
    print sortkeys[i], myArray[sortkeys[i]];
  }
}
...