Сортировка ассоциативного массива с AWK
Здесь мой массив (gawk script):
myArray["peter"] = 32
myArray["bob"] = 5
myArray["john"] = 463
myArray["jack"] = 11
После сортировки мне нужен следующий результат:
bob 5
jack 11
peter 32
john 463
Когда я использую "asort", индексы теряются. Как сортировать по значению массива без потери индексов? (Мне нужны упорядоченные индексы на основе их значений)
(Мне нужно получить этот результат только с awk/gawk, а не с оболочкой script, perl и т.д.)
Если мой пост недостаточно ясен, вот еще одно сообщение, объясняющее ту же проблему: http://www.experts-exchange.com/Programming/Languages/Scripting/Shell/Q_26626841.html)
Заранее спасибо
Обновление:
Спасибо вам обоим, но мне нужно сортировать по значениям, а не по индексам (я хочу, чтобы упорядоченные индексы соответствовали их значениям).
Другими словами, мне нужен этот результат:
bob 5
jack 11
peter 32
john 463
not:
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")
}
Конечно, меня интересует, есть ли у вас что-то более чистое...
Спасибо за ваше время
Ответы
Ответ 1
Edit:
Сортировка по значениям
О! Чтобы отсортировать значения, это бит kludge, но вы можете создать временный массив, используя конкатенацию значений и индексов исходного массива в качестве индексов в новом массиве. Затем вы можете 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]]
}
Ответ 2
Используйте команду сортировки Unix с каналом, сохраняя код Awk простым и следуя философии Unix
Создайте входной файл со значениями, разделенными запятой
питер, 32
Гнездо, 11
джон, 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 данные
боб, 5
Гнездо, 11
питер, 32
john, 463
Ответ 3
PROCINFO["sorted_in"] = "@val_num_desc";
Перед повторением массива используйте приведенный выше оператор. Но он работает в awk версии 4.0.1. Он не работает в awk версии 3.1.7.
Я не уверен, в какой промежуточной версии он появился.
Ответ 4
Авторы языка программирования Awk предоставляют функцию quicksort, доступную в Интернете.
Я думаю, вы бы сделали что-то вроде этого.
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]];
}
}
Ответ 5
И простой ответ...
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]]
}
}