1

This should be simple, but it's not I'm trying to generate a json with a loop inside it. Something like this:

all_urls = ["example.com/homepage", "example.com/another", "example.com"]
hash = {
    "type":"URLS",
    "Params":{
        "rules":{
            "condition":"AND",
            "rules":[
                begin
                    all_urls.each do |url|
                        {
                            "condition":"AND",
                            "rules":[
                                {
                                    "value":[
                                        "data",
                                        "^https{0,1}:\\/\\/#{url}\\/{0,1}$"
                                    ]
                                },
                                {
                                    "id":"@timestamp.age",
                                }
                            ]
                        }
                    end
                end
            ]
        }
    }
}

I want to get:

    {
        "type":"URLS",
        "Params":{
            "rules":{
                "condition":"AND",
                "rules":[
                    {
                        "condition":"AND",
                        "rules":[
                            {
                                "value":[
                                    "data",
                                    "^https{0,1}:\\/\\/example.com/homepage\\/{0,1}$"
                                ]
                            },
                            {
                                "id":"@timestamp.age",
                            }
                        ]
                    },
                    {
                        "condition":"AND",
                        "rules":[
                            {
                                "value":[
                                    "data",
                                    "^https{0,1}:\\/\\/example.com/another\\/{0,1}$"
                                ]
                            },
                            {
                                "id":"@timestamp.age",
                            }
                        ]
                    },
                    {
                        "condition":"AND",
                        "rules":[
                            {
                                "value":[
                                    "data",
                                    "^https{0,1}:\\/\\/example.com\\/{0,1}$"
                                ]
                            },
                            {
                                "id":"@timestamp.age",
                            }
                        ]
                    }
                ]
            }
        }
    }

Tried using map, tried many things. Just either getting errors or getting an array thrown back to me.

EDIT: I'm going to provide the exact json structure I need to produce to be clearer.

UPDATE

Using Damian's suggestion, I am very close to getting this to work, but I only manage return the last result in the array. The first two get overwritten with the merge! probably? See below:

a = ["example.com/homepage", "example.com/another", "example.com"]
hash = {
    "type":"condition",
    "Params":{
        "rules":{
            "condition":"AND",
            "rules":
                (
                    a.inject(Hash.new) do |hash_container, element|
                        hash_container.merge!({
                            "condition":"AND",
                            "rules":[
                                {
                                    "value":[
                                        "^https{0,1}:\\/\\/#{element}\\/{0,1}$"
                                    ]
                                },
                            ]
                        })
                    end
                )
        }
    }
}

I'm getting:

  => {:type=>"condition", :Params=>{:rules=>{:condition=>"AND", :rules=>{:condition=>"AND", :rules=>[{:value=>["^https{0,1}:\\/\\/example.com\\/{0,1}$"]}]}}}}   

UPDATE 3

It seems my main mistake was to assume that "rules" in my call was always an array, while in fact, it was sometimes an array and sometimes an object. Hence my confusion.

Both of the answers below are good solution and I ended up using Damian's inject of a hash to generate a hash within a loop, which was key to making it work.

What I ended up doing was creating that "Damian" array, then using #Unshift to prepend more objects to that array, and finally add that array within my call.

7
  • You can see the update, I made it too with each loop Commented Jul 17, 2018 at 22:21
  • Damian, you are close. I believe I was not clear enough in my explanation. Would you please adjust your answer accordingly? Commented Jul 17, 2018 at 22:58
  • @Ben I'm confused by your use of #drop in your example. Using that drop the first index from the array it returns, thus your example would only ever return ["example.com"] Commented Jul 17, 2018 at 23:05
  • @ActiveModel_Dirty, you are absolutely right. Adjusted my question. Sorry. Commented Jul 17, 2018 at 23:07
  • @Ben it's no problem, but just to be clear I still don't think it's going to give you what you want. Based on the post, out of that array of three domains, you want the hash to be populated with "example.com" and "example.com/homepage". Given an array of ["example.com/homepage", "example.com/another", "example.com"], calling #drop(1) will return ["example.com/another", "example.com"], leaving off "example.com/homepage" (so it won't ever make it into the loop). I'm working on an answer for you, but can I ask why not just call all_urls.each? are you purposefully wanting to drop the 1st elem? Commented Jul 17, 2018 at 23:16

2 Answers 2

2

Edit accord edited question

You can execute a function inside a hash without problems, I think the problem is how are you using the hash and the function. Look (I will use reduce instead a foreach because I like it more, but it's the same):

all_urls = ["example.com/homepage", "example.com/another", "example.com"]
hash = {
    "type":"condition",
    "Params":{
        "rules":{
            "condition":"AND",
            "rules":
            all_urls.map {|element|
              h = Hash.new
              h.merge!({
                      "condition":"AND",
                      "rules":[
                          {
                              "value":[
                                  "^https{0,1}:\\/\\/#{element}\\/{0,1}$"
                              ]
                          },
                      ]
                  })
            }
        }
    }
}

Result is:

{: type => "condition",: Params => {: rules => {: condition => "AND",: rules => [{: condition => "AND",
        : rules => [{: value => ["^https{0,1}:\\/\\/example.com/homepage\\/{0,1}$"]
        }]
      }, {: condition => "AND",
        : rules => [{: value => ["^https{0,1}:\\/\\/example.com/another\\/{0,1}$"]
        }]
      }, {: condition => "AND",
        : rules => [{: value => ["^https{0,1}:\\/\\/example.com\\/{0,1}$"]
        }]
      }]
    }
  }
}

Same thing with each :

all_urls = ["example.com/homepage", "example.com/another", "example.com"]
hash = {
    "type":"condition",
    "Params":{
        "rules":{
            "condition":"AND",
            "rules":
            begin
                  res_lis = []
                  all_urls.each do |element|
                    h = Hash.new
                    h.merge!({
                        "condition":"AND",
                        "rules":[
                            {
                                "value":[
                                    "^https{0,1}:\\/\\/#{element}\\/{0,1}$"
                                ]
                            },
                        ]
                    })
                    res_lis.push(h)
                  end
                  res_lis
            end
          }
      }
}

Same result:

{: type => "condition",: Params => {: rules => {: condition => "AND",: rules => [{: condition => "AND",
        : rules => [{: value => ["^https{0,1}:\\/\\/example.com/homepage\\/{0,1}$"]
        }]
      }, {: condition => "AND",
        : rules => [{: value => ["^https{0,1}:\\/\\/example.com/another\\/{0,1}$"]
        }]
      }, {: condition => "AND",
        : rules => [{: value => ["^https{0,1}:\\/\\/example.com\\/{0,1}$"]
        }]
      }]
    }
  }
}
Sign up to request clarification or add additional context in comments.

5 Comments

Damian, I need whatever follows "{: type => "condition",: Params => {: rules => {: condition => "AND",: rules =>", which for now is an array, to be a list of hashes separated by commas (... rules => {: value => [...]},{...},{...}). NOT an array of hashes. If you can help with that, it will be awesome since I spent the entire day trying to get it right and I can't.
@Ben but.. what’s the difference for you of a list of hashes and an array of hashes? In ruby list and array is pretty much the same. Maybe I’m not understanding the output that you are expecting :/
I am sending this as an API call. It expects a complex structure that is too long to write down here. Instead of an array, it is using comma separated hashes. It looks like mission impossible for Ruby to produce several instances that are not inside an array. Can you think of a way I can do this?
Finally found my mistake and used your solution to fix it. See last update to my post. Thanks!
@Ben Fantastic! I feel great for you! Don't forget to accept the answer if it was useful for you, if you want to. Cheers! I have always loved to help, don't thank me
1

Give #map a shot:

a = ["example.com/homepage", "example.com/another", "example.com"]
hash = {
    "type":"condition",
    "Params":{
      "rules": {
        "condition":"AND",
        "rules":
          a.map do |url|
            {
              "condition":"AND",
              "rules": {
                "value":[
                  "^https{0,1}:\\/\\/#{url}\\/{0,1}$"
                ]
              },
            }
          end
        }
      }
    }

Output:

 1|    {:type=>"condition",
 2|     :Params=>
 3|      {:rules=>
 4|        {:condition=>"AND",
 5|         :rules=>[
 6|            {:condition=>"AND",
 7|             :rules=>{
 8|               :value=>["^https{0,1}:\\/\\/example.com/homepage\\/{0,1}$"]
 9|              }
10|            },
11|            {:condition=>"AND",
12|             :rules=>{
13|               :value=>["^https{0,1}:\\/\\/example.com/another\\/{0,1}$"]
14|             }
15|           },
16|           {:condition=>"AND",
17|             :rules=>{
18|               :value=>["^https{0,1}:\\/\\/example.com\\/{0,1}$"]
19|             }
20|           }
21|         ]
22|       }
23|    }

Compared to your desired output, it is largely the same, if anything there are less arrays... So I'm not sure where the disconnect is:

    1|        {"type":"URLS",
    2|         "Params":{
    3|           "rules":{
    4|           "condition":"AND",
    5|           "rules":[
    6|             {"condition":"AND",
    7|              "rules":[
    8|                {
    9|                  "value":["^https{0,1}:\\/\\/example.com/homepage\\/{0,1}$"]
   10|                 }
   11|               ]
   12|              },
   13|              {"condition":"AND",
   14|               "rules":[
   15|                 {
   16|                   "value":["^https{0,1}:\\/\\/example.com/another\\/{0,1}$"]
   17|                 }
   18|               ]
   19|              },
   20|              {"condition":"AND",
   21|               "rules":[
   22|                 {
   23|                   "value":["^https{0,1}:\\/\\/example.com\\/{0,1}$"]
   24|                 }
   25|               ]
   26|             }
   27|           ]
   28|         }
   29|       }
   30|     }

6 Comments

This may work, but seems like an overkill. I prefer not creating classes for every loop I may have in a hash. I am very close to a solution, please see my new edit.
@Ben f you just want to do this as a one-off, use #map, I updated my answer with a simple solution that will work. But I do want to note that I disagree. For a one-off, sure doing it inline as you desire is fine. But once you have multiple JSON structures with varying logic in the loops, having it all consolidated in one place is a huge time-saver and much more easily maintained. Already, this is a difficult thing to parse because you have a lot of the same keys and a deeply nested structure. Also, the class wasn't representative of a loop but the entire Json structure.
I already tried that. #map created an array of hashes. I need it as hashes without the array containing it. See my updated Update :)
@Ben The example in your question had the hashes contained in an array, I thought (as does the other answer). It can't work without that as you cannot have a hash containing multiple of the same key, it defeats the purpose of using a hash. If you must, look into Hash#compare_by_identity, but you won't be able to reference the hash by key (the values essentially become the keys): apidock.com/ruby/Hash/compare_by_identity Otherwise you will just overwrite the key each time.
@Ben I added some line numbers and your desired output from the question. If you're still trying to figure this out, can you help me understand what the exact output you're looking for is? This matches everything except it does not contain the "value" hash in an array (considering you're trying to rid yourself of arrays, I'm assuming that's not the issue), but that's just because I've omitted the id field for brevity. You were saying you don't want the array that starts on line 5 (of my output example)?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.