Ruby - getting value in logstash - ruby-on-rails

i have a field and value output looks like
A::field_1_2_3_4_22_5_6_7_8_365 => 6
because the field name is "dynamical" because of contain ip and port.
how to using ruby to get value from field name and add field for it ? it's will be looks like
A::field => 6
bIP => 1.2.3.4
bport => 22
cIP => 5.6.7.8
cport => 365
any help will be appreciated!!thanks

Here is an answer for a very similar question: logstash name fields dynamically
For this precise case, the ruby filter needs to be a bit more involved in order to capture different things
filter {
ruby {
code => "
newhash = {}
event.to_hash.each {|key, value|
re = /(\w::[a-z]+)_(\d+_\d+_\d+_\d+)_(\d+)_(\d+_\d+_\d+_\d+)_(\d+)/
if key =~ re then
field, bIP, bport, cIP, cport = key.match(re).captures
newhash[field] = event[key]
newhash['bIP'] = bIP.gsub('_', '.')
newhash['bport'] = bport
newhash['cIP'] = cIP.gsub('_', '.')
newhash['cport'] = cport
event.remove(key)
end
}
newhash.each {|key,value|
event[key] = value
}
"
}
}
So if you have a field like "A::field_1_2_3_4_22_5_6_7_8_365" => "6" in your event, your event will then contain the following fields:
{
"A::field" => "6",
"bIP" => "1.2.3.4",
"bport" => "22",
"cIP" => "5.6.7.8",
"cport" => "365"
}

You can use the the following code:
field_name = 'A::field_1_2_3_4_22_5_6_7_8_365'
fields = field_name.split("_")
bID = "#{fields[1]}.#{fields[2]}.#{fields[3]}.#{fields[4]}"
bport = "#{fields[5]}"
bID = "#{fields[6]}.#{fields[7]}.#{fields[8]}.#{fields[9]}"
bport = "#{fields[10]}"

Maybe i've not understooden this quite correctly but you can use arrays and method .push for adding:
Array=[1, 2, 5, 7]
#n = [3, 4, 6]
Array.push(#n) => Array=[1, 2, 3, 4, 5, 6, 7]

Related

How can I deep transform key or value in a hash/array only for the first occurence

I have the following example
input = {
"books2" => [1, {a: 1, b: "seller35" }],
"books3" => { "a" =>[{"5.5"=>"seller35", "6.5" => "foo"}]}
}
And I would like to deep transform values in this has that matches seller35. However, only for the first occurrence. So for b: "seller35". "5.5"=>"seller35" should stay intact.
Ideally, for key, value and/or element in array.
I looked at: https://apidock.com/rails/v6.0.0/Hash/_deep_transform_keys_in_object for inspiration but couldn't figure out a solution. That does it for all
input.deep_transform_values { |value| value == "seller35" ? "" : value }
=> {"books2"=>[1, {:a=>1, :b=>""}], "books3"=>{"a"=>[{"5.5"=>"", "6.5"=>"foo"}]}}
Thanks!
You can create a done boolean value to keep track of whether a replacement has been done or not yet and return an appropriate value from the block:
require "active_support/core_ext/hash/deep_transform_values"
def deep_transform_values_once(hash:, old_value:, new_value:)
done = false
hash.deep_transform_values do |value|
if !done && value == old_value
done = true
new_value
else
value
end
end
end
input = {
"books2" => [1, { a: 1, b: "seller35" }],
"books3" => { "a" => [{ "5.5" => "seller35", "6.5" => "foo" }] },
}
p deep_transform_values_once(hash: input, old_value: "seller35", new_value: "")
Output:
{"books2"=>[1, {:a=>1, :b=>""}], "books3"=>{"a"=>[{"5.5"=>"seller35", "6.5"=>"foo"}]}}

Search by array of values in .select query method

I'm having this type of search:
values = ModelName.find(:all, :conditions => ['attr_id IN (SELECT attr_id FROM srv_type_attr WHERE id IN (?))', serv_objt_attr.collect(&:stya_id)])
Witch returns me an array of needed values:
[33458, 33438]
Next i need to check if record exists with select:
serv_objt_attr.select {|array| array.stya_id == values.collect(&:attr_id).uniq}
This is an example what i'm thinking off.
So how to do it with select, so he would walk through all values witch i'm getting from values.
I know that i could to something like
values.collect(&:attr_id).uniq do |val|
serv_objt_attr.select {|array| array.stya_id == val}
end
But i do not thing that this is a good option.
Ruby 1.8.7
Rails 2.3.4
This is a good case for the set intersection operator:
values = ModelName.find(:all, :conditions => ['attr_id IN (SELECT attr_id FROM srv_type_attr WHERE id IN (?))', serv_objt_attr.collect(&:stya_id)])
values & Set.new(serv_objt_attr.map(&:stya_id)
Here's what the & does:
>> values = [1,2,3]
=> [1, 2, 3]
>> other_array = [1,5,9,3]
=> [1, 5, 9, 3]
>> values & other_array
=> [1, 3]

Ruby Unable to map values to array of hash, searching for alternative

I have an array of hashes:
data1 = [{:first => 1, :second => 'whatever'},{:first => 2, :second => 'something'}]
test_merge = {'whatever' => {:third => 1, :fourth => 1}, 'something' => {:third => 2, :fourth => 2}}
I want this output:
[{:first=>"MODIFIED", :second=>"whatever", :test_field=>"added", :third=>1, :fourth=>1, "sdsds"=>"sdsds", "2x4"=>8}, {:first=>"MODIFIED", :second=>"something", :test_field=>"added", :third=>2, :fourth=>2, "sdsds"=>"sdsds", "2x4"=>8}]
I did map and merge:
data2 = data1.map do |d|
d[:test_field] = 'added'
d[:first] = 'MODIFIED'
d2 = test_merge[d[:second]]
d = d.merge(d2)
d['sdsds'] = 'sdsds'
d['2x4'] = 2*4
end
I got:
data1 #=> [{:first=>"MODIFIED", :second=>"whatever", :test_field=>"added"}, {:first=>"MODIFIED", :second=>"something", :test_field=>"added"}]
data2 #=> [8, 8]
This can be fixed if I do
data2 = data1.map do |d|
d[:test_field] = 'added'
d[:first] = 'MODIFIED'
d2 = {:test_field_two => 'notadded'}
d = d.merge(d2)
d['sdsds'] = 'sdsds'
d['2x4'] = 2*4
d #or d=d
end
But why do I have to insert that d in the end (d or d=d)? It looks ugly to write d in the end. Isn't there an alternative way to do it, I want the exact output as mentioned above?
Edit: 3 Jan 2017 (Don't read this edit if you are reading the question for first time, everything is mentioned above)
I have to necessarily use merge inside the .map, because I have got another hash as follows:
test_merge = {'whatever' => {:third => 1, :fourth => 1}, 'something' => {:third => 2, :fourth => 2}}
which needs to be merged like this inside the .each (merge doesn't work in .each unfortunately so I have to use map) or .map
d = d.merge(test_merge[d[:second]])
Also '2x4' , :first and other keys are important which needs to be set/modified.
And if I need to modify d[:first] , it should get modified.
I tried .each as mentioned by steve,
data1 = [{:first => 1, :second => 'whatever'},{:first => 2, :second => 'something'}]
test_merge = {'whatever' => {:third => 1, :fourth => 1}, 'something' => {:third => 2, :fourth => 2}}
data1.each do |d|
d[:test_field] = 'added'
d[:first] = 'MODIFIED'
d2 = test_merge[d[:second]]
d = d.merge(d2)
d['sdsds'] = 'sdsds'
d['2x4'] = 2*4
end
And this is the result I got:
[{:first=>"MODIFIED", :second=>"whatever", :test_field=>"added"}, {:first=>"MODIFIED", :second=>"something", :test_field=>"added"}]
That is not my expected output. Is there any hack to this?
When doing a map with a block the last executed line of the block is what's returned in the new array.
So if the last line is...
d['2x4'] = 2*4
Then it's 2*4 (8) that's returned.
Setting the last line of the block to...
d
...means that the value of the d variable (the entire hash) is returned.
You don't need to use map if you just want to change the hashes and don't need an array returned. Use each instead. (Edited to take into account Eric's observation about shallow duplication).
data2 = data1.map{|d| d.dup}
data2.each do |d|
d[:test_field] = 'added'
d[:test_field_two] = 'notadded'
d['sdsds'] = 'sdsds'
d['2x4'] = 2*4
end
Hash#merge returns a merged Hash without changing the original one, so you can add new |key,value| pairs and return the desired hash directly :
data2 = data1.map do |h1|
h1.merge(:test_field => 'added',
:test_field_two => 'not_added',
'sdsds' => 'sdsds',
'2x4' => 2 * 4)
end
data2 is now :
[{:first=>1,
:second=>"whatever",
:test_field=>"added",
:test_field_two=>"not_added",
"sdsds"=>"sdsds",
"2x4"=>8},
{:first=>2,
:second=>"something",
:test_field=>"added",
:test_field_two=>"not_added",
"sdsds"=>"sdsds",
"2x4"=>8}]
while data1 still is :
[{:first=>1, :second=>"whatever"}, {:first=>2, :second=>"something"}]
I think this might be better! Though I am not really sure if i understood the question correctly. Hope this helps.
data1 = [{:first => 1, :second => 'whatever'},{:first => 2, :second => 'something'}]
d2 = {:test_field_two => 'notadded', "sdsds"=>"sdsds", "2x4"=>2*4}
data2 = data1.map do |d| d.merge(d2) end
#=> [{:first=>1, :second=>"whatever", :test_field_two=>"notadded", "sdsds"=>"sdsds", "2x4"=>8}, {:first=>2, :second=>"something", :test_field_two=>"notadded", "sdsds"=>"sdsds", "2x4"=>8}]

Convert group count from activerecord into hash with multiple group stages

I'm trying to get some statistics.
Model.where(status:#statuses).group(:sub_group, :status).count
This returns me
{
["sub_group1", "status1"] => 3},
["sub_group2", "status3"] => 7,
["sub_group1", "status2"] => 5, ....etc }
I want to merge them so each element has a unique subgroup.
e.g. a hash like:
{
"sub_group1" => {"status1" => 3, "status2" => 5,
"sub_group2" => {"status3" => 7},
}
or an array
[
["subgroup1", {"status1" = 3, "status2" => 5}],
["subgroup2".....
]
i.e. I want all those terms to be merged with sub_group as the primary header so that I can get the results for subgroup in one item.
Brain not working today....
You can try with merge!:
result = Model.where(status: #statuses).group(:sub_group, :status).count
result.reduce({}) do |collection, (attributes, count)|
collection.merge!(attributes[0] => { attributes[1] => count }) do |_, prev_value, next_value|
prev_value.merge!(next_value)
end
end
Demonstration
I suppose you could do something like this:
res = Model.where(status:#statuses).group(:sub_group, :status).count
nice_hash = {}
res.each do |key, count|
nice_hash[key[0]] = {} unless nice_hash[key[0]]
nice_hash[key[0]][key] = count
end
After this nice_hash should be on the desired format
Please try with below code.
a = Model.where(status:#statuses).group(:sub_group, :status).count
res = {}
a.each do |k,v|
c={}
if res.has_key?(k[0])
c[k[1]]=v
res[k[0]]=res[k[0]].merge(c)
else
c[k[1]]=v
res[k[0]]=c
end
end
I believe the answer to your question should be this or this should guide in the right direction:
Model.where(status:#statuses).group_by(&:sub_group)
If you need the only the statuses as you mentioned, you could do:
Model.where(status:#statuses).select(:status, :sub_group).group_by(&:sub_group)

Ruby - how to replace key in a hash by a key from different one?

I have this hash:
CARS = {"Audi" => 0,
"BMW" => 1,
...}
And this output from ActiveRecord (#top_cars):
{1=>18, 0=>17, 3=>13, 5=>10, 2=>5, 4=>1}
How do I replace the keys from #top_cars by the car names from CARS?
Thank you
EDIT:
So the desired output would be like {"BMW"=>18, "Audi"=>17, "Renault"=>13, "Mercedes"=>10, "Ford"=>5, "Porsche"=>1}
This would do the trick:
#top_cars.map {|key, value| [CARS.key(key), value]}.to_h
possible solution:
#top_cars.inject({}) {|memo, (key,value)| memo.merge(CARS.key(key) => value)}
You could merge cars with itself:
cars = { "Audi" => 0,
"Mercedes" => 1,
"Ford" => 2,
"Renault" => 3,
"BMW" => 4,
"Porsche" => 5
}
top_cars = {1=>18, 0=>17, 3=>13, 5=>10, 2=>5, 4=>1}
cars.merge(cars) { |*,n| top_cars[n] }
#=> {"Audi"=>17, "Mercedes"=>18, "Ford"=>5, "Renault"=>13, "BMW"=>1, "Porsche"=>10}
This uses the form of Hash#merge where a block is employed to determine the values of keys that are present in both hashes being merged, which here is all the keys.

Resources