我有 gawk (4.++) 的真正多维数组,我想将其“展平”(转换为 OLD/simulated – pre-gawk 4.x)数组。
arr["a"]["a"] = 21
arr["a"]["b"] = 21
arr["b"]["a"] = 2
arr["b"]["b"] = 3
arr["c"]["a"] = 4
转换为
arrFlat["a", "a"] = 21
arrFlat["a", "b"] = 21
arrFlat["b", "a"] = 2
arrFlat["b", "b"] = 3
arrFlat["c", "a"] = 4
我一直在研究处理多维数组的多个线程,包括
还有一些处理 walk_array 等问题。我确信这个问题以前有人问过,但我找不到正确的递归调用来将一个数组表示转换为另一个数组表示。我需要这个来简化多维数组的“按值排序”(数字或其他)。
显然,数组(源 arr)的索引“深度”并不像示例中那样限制为 2。
有谁能分享任何金块吗!
5
最佳答案
3
我认为我找到了一种方法来调整@EdMorton“copy_array”的实现,使其变成可以“展平”真正的多维数组的东西:
$ cat flatten.awk
BEGIN {
arr["a"]["a"] = 21
arr["a"]["b"] = 21
arr["b"]["a"] = 2
arr["b"]["b"] = 3
arr["c"]["a"] = 4
arr["d"]["x"]["y"]= 90
arr["d"]["x"]["z"]= 95
walk_array(arr, "arr")
PROCINFO["sorted_in"]="@val_num_asc"
flatten_array(arr, flatten)
print "----------"
walk_array(flatten, "flatten")
}
function flatten_array(orig, flatten, idx, i)
{
for (i in orig) {
if (isarray(orig[i])) {
flatten_array(orig[i], flatten, (idx=="" && idx==0) ? i : (idx SUBSEP i) )
}
else {
#printf("idx->[%s]\n", idx) > "/dev/stderr"
flatten[idx SUBSEP i] = orig[i]
}
}
}
function walk_array(arr, name, i)
{
for (i in arr) {
if (isarray(arr[i]))
walk_array(arr[i], (name "[" i "]"))
else
printf("%s[%s] = %s\n", name, i, arr[i])
}
}
$ gawk -f flatten.awk
arr[a][a] = 21
arr[a][b] = 21
arr[b][a] = 2
arr[b][b] = 3
arr[c][a] = 4
arr[d][x][y] = 90
arr[d][x][z] = 95
----------
flatten[ba] = 2
flatten[bb] = 3
flatten[ca] = 4
flatten[aa] = 21
flatten[ab] = 21
flatten[dxy] = 90
flatten[dxz] = 95
不是特别优雅,但是…除非其他人可以提供替代方案……
5
-
1由于你必须以递归方式“取消构建”多维数组的索引,因此不确定你是否能够获得“更”优雅的效果;在构建原始数组的同时构建扁平数组可能是你能获得的最优雅的方式;由于
flatten[]
是一维数组,因此你可以放弃walk_array()
对单个for i in flatten printf ...
循环的调用;话虽如此……它有效,如果用户理解其工作原理walk_array()
,copy_array()
那么这是一个易于理解的解决方案;赞
– -
1看起来不错,但
""
对于数组索引来说是一个有效值,因此,您不应该先进行第一次调用flatten_array(arr, flatten, "")
,然后用 进行测试,而(length(idx) ? (idx SUBSEP i) : i)
应该先进行第一次调用flatten_array(arr, flatten)
,然后测试变为(idx=="" && idx==0 ? i : (idx SUBSEP i))
。您可能希望命名扁平数组,flat
而不是只是flatten
为了稍微提高清晰度。
–
-
1@EdMorton 所有评论都很有效——感谢!
– -
@EdMorton:
idx=="" && idx==0
– 一个数组不能同时具有空字符串("")
和空值( idx == "" && idx == 0 )
作为索引(否则arr[""]
或("" in arr)
成为歧义语法),因此 IIRC 仅针对空字符串进行测试就足够了。(但将(1)数字或字符串零与(2)空字符串或空值配对是完全没问题的)
– -
@RAREKpopManifesto 数组不可以,因为所有数组索引都是字符串,但函数参数可以,并且 OP 通过使用设置为
idx == ""
作为参数的函数来测试第一次与后续对函数的调用,但是由于是有效的数组索引,因此无法工作。idx
""
""
–
|
使用标准作为新函数的模板flatten_array()
:
awk '
BEGIN { #### sample multi-dimensional array
arr["a"]["a"] = 21
arr["a"]["b"] = 21
arr["b"]["a"] = 2
arr["b"]["b"] = 3
arr["c"]["a"] = 4
arr["d"] = 19
arr["e"]["f"]["g"]["h"] = 14
flatten_array(arr, "arr")
#### OP mentions needing to sort the (flattened) array by value ...
print "############ @val_num_desc"
PROCINFO["sorted_in"] = "@val_num_desc" # sort arrFlat[] by numeric value in descending order
for (ndx in arrFlat)
printf "arrFlat[%s] = %s\n", ndx,arrFlat[ndx]
print "############ @val_num_asc"
PROCINFO["sorted_in"] = "@val_num_asc" # sort arrFlat[] by numeric value in ascending order
for (ndx in arrFlat)
printf "arrFlat[%s] = %s\n", ndx,arrFlat[ndx]
}
function flatten_array(arr, name, ndx, i)
{
for (i in arr) {
new_ndx = ndx (ndx=="" ? "" : ",") i # "," is a literal comma; replace "," with SUBSEP if you wish to use the system default delimiter
if (isarray(arr[i]))
flatten_array(arr[i], (name "[" i "]"), new_ndx )
else
arrFlat[new_ndx] = arr[i]
}
}
'
笔记:
- 回复:索引分隔符 – 逗号与 SUBSEP:
new_ndx = ndx (ndx=="" ? "" : ",") i
– 这使用文字逗号作为索引分隔符,这在打印数组索引时是可见的new_ndx = ndx (ndx=="" ? "" : SUBSEP) i
– 这将使用 awk 的标准索引分隔符\034
,但它通常不会在打印到 stdout 时显示"a" SUBSEP "b"
通常会显示为,ab
但是如果你通过管道传输,od -c
你会看到a \034 b
- 如果索引不包含逗号,那么 OP 应该能够根据显示(标准输出)要求使用任一分隔符(逗号、SUBSEP)
- 回复:目标数组名称:
- 目标数组名称( )在函数
arrFlat[]
中硬编码flatten_array()
- 目标数组名称的参数化留给读者练习
- 提示:请参阅或,了解参数化数组名称的方法
这将生成:
############ @val_num_desc
arrFlat[a,b] = 21
arrFlat[a,a] = 21
arrFlat[d] = 19
arrFlat[e,f,g,h] = 14
arrFlat[c,a] = 4
arrFlat[b,b] = 3
arrFlat[b,a] = 2
############ @val_num_asc
arrFlat[b,a] = 2
arrFlat[b,b] = 3
arrFlat[c,a] = 4
arrFlat[e,f,g,h] = 14
arrFlat[d] = 19
arrFlat[a,a] = 21
arrFlat[a,b] = 21
2
-
1@markp_fuso,谢谢 – 和我采取的方法差不多。感谢您的努力!
– -
1类似的评论
ndx==""
。您还可以考虑设置SUBSEP=","
然后使用SUBSEP
而不是硬编码","
,并添加注释以将其替换为SUBSEP
。
–
|
假设您不需要能够以交互方式执行此操作,也许这就是您要找的。我试图弄清楚如何将数组作为输入传递给此脚本,因为我认为这无法以交互方式工作,尽管也许其他人可以采用它并对其进行一些改进。
编辑:好的,我刚刚写了 shell 脚本。
#!/bin/bash
# In case you need to do this programmatically
if [ -z "$1" ]; then
echo "Usage: $0 '<input array to flatten>'"
echo "Example: $0 'arr[\"a\"][\"a\"]=21; \
arr[\"a\"][\"b\"]=21; arr[\"b\"][\"a\"]=2; \
arr[\"b\"][\"b\"]=3; arr[\"c\"][\"a\"]=4; \
arr[\"c\"][\"d\"][\"e\"]=10;'"
exit 1
fi
array_definition="$1"
# Temporary file
gawk_script="temp_flatten_sort.awk"
# create a temporary custom hardcoded script
cat << EOF > "$gawk_script"
# Recursive function to flatten multi-dimensional arrays
function flatten_array(src, dest, prefix, key, flat_key) {
for (key in src) {
if (isarray(src[key])) {
flat_key = (prefix == "") ? key : prefix "," key
flatten_array(src[key], dest, flat_key)
} else {
flat_key = (prefix == "") ? key : prefix "," key
dest[flat_key] = src[key]
}
}
}
BEGIN {
$array_definition
delete flattened_array # Initialize the flattened array
flatten_array(arr, flattened_array)
# Sort by values
n = 0
for (k in flattened_array) {
n++
keys[n] = k
}
for (i = 1; i <= n; i++) {
for (j = i + 1; j <= n; j++) {
if (flattened_array[keys[i]] > flattened_array[keys[j]]) {
temp = keys[i]
keys[i] = keys[j]
keys[j] = temp
}
}
}
# Print
for (i = 1; i <= n; i++) {
print "flattened_array[\"" keys[i] "\"] =", flattened_array[keys[i]]
}
}
EOF
# exec
gawk -f "$gawk_script"
# cleanup
rm "$gawk_script"
现在,你可以运行它:
$./flatten.sh 'arr["a"]["a"]=21; arr["a"]["b"]=21; arr["b"]["a"]=2; arr["b"]["b"]=3; arr["c"]["a"]=4; arr["c"]["d"]["e"]=10;'
2
-
感谢分享,@Chev_603 – 与我的尝试类似。
– -
嗯,其他的更好。我创建了一个包装脚本,以便您可以交互调用它。黑客之上的黑客 xD。
–
|
BEGIN{}
?它是否是从输入数据集构建的?其他什么?)–
–
gawk
代码以同时生成两个数组(arr[]
,arrFlat[]
),因为代码(显然?)可以访问此时的索引;至于轻推……您需要通过将“当前”复合索引传递给函数来构建复合索引,然后将下一级索引附加到所述复合索引的末尾,然后再传递给下一个函数调用(Duh, Mark!
?)–
–
–
|