【问题标题】:Label elements based on class they belong to根据元素所属的类标记元素
【发布时间】:2019-03-10 11:48:37
【问题描述】:

我有一个元素流 object_count.json 看起来像:

{
    "name":"apple",
    "count":10
}
{
    "name":"potato",
    "count":18
}
{
    "name":"stone",
    "count:7
}

与流分开,我有两个文件,每个文件都列出了其类对象的名称:

cat fruits.txt
  apple
  peach

cat vegetables.txt
  potato
  tomato

如何构造一个流,其中来自object_count.json 的每个对象都带有其各自的类名?即:

{
    "name":"apple",
    "count":10
    "class":"fruit"
}
{
    "name":"potato",
    "count":18
    "class":"vegetable"
}
{
    "name":"stone",
    "count":7
    "class":"other"
}

我们可以假设object_count.json 中的每个对象名称只有一个条目。


我最喜欢这样的解决方案:

  • 适用于 3、4 和更多类 - 每个仍然列在单独的文件中;如果每个类都是硬编码的就可以了
  • object_count.json 被视为输入流,而 {fruits|vegetables}.txt 分别作为单独的 --rawfile 参数提供

JQ 报告的版本:

 $ jq --version
 jq-1.6

我设法将类文件作为命令行参数提供,但未能找到合适的 join / in 表达式来解决问题:

cat object_count.json | jq -n --rawfile fruits "fruits.txt" '($fruits | split("\n") | map(select(. != "")) | sort) as $frts | inputs'

【问题讨论】:

    标签: json dictionary jq


    【解决方案1】:

    根据更新后的问题,这里有一个避免slurping object_count.json 内容的解决方案。

    dictionary.jq

    def trim: sub("^ +"; "") | sub(" +$"; "");
    
    def dictionary(s):
      reduce (s | trim | select(length>0)) as $in ({};
         (input_filename | sub(".txt";"") | sub("s$";"")) as $class
         | .[$in] = $class );
    
    dictionary(inputs) 
    

    调用

    jq --argfile class <(jq -n -R -f dictionary.jq *.txt)  '
     .class = ($class[.name] // "other")'  object_count.json
    

    后记

    可能需要使用“其他”作为默认类名,但如果不是,那么在这种情况下,最好顺其自然并使用 JSON 的 null 作为对象的默认值键。

    【讨论】:

    • 非常感谢!你能详细说明一下:它是如何工作的? trim 似乎删除了空行和空行;字典似乎从输入的 .txt 文件中构造了一组数组(?);最后一个子句不是很直观:| .[$in] = $class。调用或多或少是直观的;我只是想知道整个事情是否可以用一个 JQ 流程来完成(虽然不是什么大不了的事)。仍然很好奇为什么您不喜欢使用 injoin 运算符,我最初希望通过示例了解它们:-)
    • sub 就像 awk 的 sub;您可以在 jq 手册 (stedolan.github.io/jq/manual) 中了解它和其他详细信息。不得不处理多个硬编码的类名对我来说太乏味了。我对简单的解决方案更感兴趣,即使只是回想起来也很清楚。在这种情况下,我们受到 jq 的一些不幸限制的限制。
    【解决方案2】:

    [这是对原始问题的回应。它允许任意多个“类”文件,并且只需要一次调用 jq。对于不涉及“slurping”JSON 流的解决方案,请参阅本页其他地方。]

    这里的棘手部分是管理未知数量的 .txt 文件。在这里,我们假设我们可以编写 '*.txt' 来捕获它们,因此 jq 的调用将如下所示:

    jq -n -R -f program.jq --slurpfile input object_count.json *.txt
    

    为了构建字典,我们将使用以下辅助函数:

    def trim: sub("^ +"; "") | sub(" +$"; "");
    
    # construct the dictionary
    def dictionary(s):
      reduce (s | trim | select(length>0)) as $in ({};
         (input_filename | sub(".txt";"") | sub("s$";"")) as $class
         | .[$in] = $class );
    

    现在任务减少到以下三行:

    dictionary(inputs) as $class
    | $input[]
    | .class = ($class[.name] // "other")
    

    【讨论】:

    • 1.我的计划是提供每个 {fruits|vegetables}.txt 作为单独的 --rawfile 参数。这样做的原因之一是 object_count.json 有点大(数百个对象),而 {fruits|vegetables}.txt 每个只有几十个,纯每行一个名称的文件。为这种情况构建 JQ 查询会使其更复杂吗?
    • 2.您能否详细说明每个辅助函数的实际工作原理?
    • 3.出于原始问题的目的,我们可以假设类的数量(以及 .txt 文件)在调用 JQ 的查询/命令中是硬编码的。
    • 如果 object_count.json 只包含几百个(甚至几千个)对象,slurping 文件就可以了。使用多个 --rawfile 参数的问题是您必须对所有类名进行硬编码。当然,还有许多其他可能性(例如,您可以将 *.txt 文件预处理为单个 JSON 文件),但为什么不坚持使用一个非常简单且显然足够的方法呢?
    • 根据我对上述 cmets 的偏好更新了原始问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多