Erlang Extract from Map - hashmap

The below is a object in a record of type
requirements = [] :: [term()],
We extract the Requirements object like below
Requirements = Records#record.requirements
And I need the lat, long, radius independently from any.latlong_radius tuple. How can we extract it out ?
"requirements": {
"supply_type": {
"$tuple": [1, {
"$tuple": ["mobile_app", "nil", "nil"]
}]
},
"has_app_name": true,
"any": {
"latlong_radius": {
"$tuple": [33.042334, -96.734884, 16093.4]
}
},
},

-module(my).
-compile(export_all).
-record(requirements, {supply_type,
has_app_name,
any} ).
get_requirements() ->
#requirements{
supply_type = #{"$tuple" => [1, 2, 3]},
has_app_name = true,
any = #{"latlong_radius" =>
#{"$tuple" => [33.042334, -96.734884, 16093.4]}
}
}.
go() ->
Requirements = get_requirements(),
io:format("requirements: ~p~n", [Requirements]),
Any = Requirements#requirements.any,
#{"latlong_radius" :=
#{"$tuple" := [Lat, Lon, Rad]}
} = Any,
io:format("Lat: ~w, Lon: ~w, Rad: ~w~n", [Lat, Lon, Rad]).
In the shell:
51> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
52> my:go().
requirements: {requirements,#{"$tuple" => [1,2,3]},
true,
#{"latlong_radius" =>
#{"$tuple" =>
[33.042334,-96.734884,16093.4]}}}
Lat: 33.042334, Lon: -96.734884, Rad: 16093.4
ok
On the other hand, if your data structure is maps all the way down (which makes everything you posted about records and list specifications irrelevant):
-module(my).
-compile(export_all).
get_requirements() ->
#{"requirements" =>
#{
"supply_type" => #{"$tuple" => [1, 2, 3]},
"has_app_name" => true,
"any" => #{"latlong_radius" =>
#{"$tuple" => [33.042334, -96.734884, 16093.4]}
}
}
}.
go() ->
Requirements = get_requirements(),
io:format("requirements: ~p~n", [Requirements]),
#{"requirements" :=
#{
"any" :=
#{
"latlong_radius" :=
#{
"$tuple" := [Lat, Lon, Rad]
}
}
}
} = Requirements,
io:format("Lat: ~w, Lon: ~w, Rad: ~w~n", [Lat, Lon, Rad]).
In the shell:
70> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}
71> my:go().
requirements: #{"requirements" =>
#{"any" =>
#{"latlong_radius" =>
#{"$tuple" => [33.042334,-96.734884,16093.4]}},
"has_app_name" => true,
"supply_type" => #{"$tuple" => [1,2,3]}}}
Lat: 33.042334, Lon: -96.734884, Rad: 16093.4
ok
However, with deeply nested maps trying to get the pattern match syntax correct is just too error prone, so I would use maps:get/2 instead:
-module(my).
-compile(export_all).
get_requirements() ->
#{"requirements" =>
#{
"supply_type" => #{"$tuple" => [1, 2, 3]},
"has_app_name" => true,
"any" => #{"latlong_radius" =>
#{"$tuple" => [33.042334, -96.734884, 16093.4]}
}
}
}.
go() ->
Map = get_requirements(),
io:format("Map: ~p~n", [Map]),
Requirements = maps:get("requirements", Map),
Any = maps:get("any", Requirements),
LLR = maps:get("latlong_radius", Any),
#{"$tuple" := [Lat, Long, Radius]} = LLR,
io:format("Lat: ~w, Lon: ~w, Rad: ~w~n", [Lat, Long, Radius]).
Much, much simpler.

Just assign variable with double quote '' like below:
Latlong_radius = Any#any.latlong_radius
Res = Latlong_radius#latlong_radius.'$tuple'

Related

How to use a declared class multiple times in puppet

I'm setting up my firewalld services via puppet codes as below. My class refer to data hiera to fetch data
class profile::base::firewalld(
Hash $zones = {},
Hash $ports = {},
Hash $services = {},
Hash $rich_rules = {},
Hash $custom_services = {},
Hash $add_services = {},
Hash $remove_services = {},
Array $zone_name = [],
){
class{'firewalld':
zones => $zones,
ports => $ports,
services => $services,
rich_rules => $rich_rules,
custom_services => $custom_services,
}
contain firewalld
#remove firewalld services (ipp-client, samba-client)
$remove_services.each | String $zone, Array[String] $services|{
$services.each | String $service |{
firewalld_service {"${service}":
ensure => absent,
service => $service,
zone => $zone,
}
}
}
#add zone zPrivate zInternet zTier1
$zone_name.each | String $zone_names |{
firewalld_zone{"${zone_names}":
ensure => present,
target => 'default',
purge_rich_rules => true,
purge_services => true,
purge_ports => true,
}
}
I want to use this class multiple times. For example. If I want to re-use the profile::base::firewalld but I dont know how to refer to hiera data with differens variables. Can someone provide my good way to solve my issue. The codes below is my approach but it does not work
class profile::bs::firewalld(
Hash $zones = {},
Hash $ports = {},
Hash $services = {},
Hash $rich_rules = {},
Hash $custom_services = {},
Hash $add_services = {},
Hash $remove_services = {},
Array $zone_name = [],
){
include profile::base::firewalld
}

Error inserting multiple rows into a PostgreSQL Ecto database using a changeset

I have this function to insert all categories into a productCategory table for a specific product.
EG one product has many categories.
In repo.ex
def insertProductCategories(conn, product, productId) do
IO.inspect(product)
changeset = Enum.each(product["categories"], fn (productCategory) ->
Api.ProductCategory.changeset(%Api.ProductCategory{c_id: productCategory["value"], p_id: productId})
end)
errors = changeset.errors
valid = changeset.valid?
case insert(changeset) do
{:ok, product} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{
successs: product
}))
{:error, changeset} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(500, Poison.encode!(%{
failure: changeset
}))
end
end
productCategory.ex
defmodule Api.ProductCategory do
use Ecto.Schema
#derive {Poison.Encoder, only: [:c_id, :p_id]}
schema "productCategories" do
field :c_id, :integer
field :p_id, :integer
end
def changeset(productCategory, params \\ %{}) do
productCategory
|> Ecto.Changeset.cast(params, [:c_id, :p_id])
|> Ecto.Changeset.validate_required([:c_id, :p_id])
end
end
This is the 2 things that log to the console when insertProductCategories runs for - the inspection of a product, and the error:
%{"brand" => "Healtheries",
"categories" => [%{"categoryId" => 1, "label" => "Meat",
"selectedAdd" => true, "selectedSearch" => false, "value" => 1},
%{"categoryId" => 1, "label" => "Dairy", "selectedAdd" => true,
"selectedSearch" => false, "value" => 2},
%{"categoryId" => 1, "label" => "Confectionary", "selectedAdd" => true,
"selectedSearch" => false, "value" => 3},
%{"categoryId" => 1, "label" => "Dessert", "selectedAdd" => true,
"selectedSearch" => false, "value" => 4},
%{"categoryId" => 1, "label" => "Baking", "selectedAdd" => true,
"selectedSearch" => false, "value" => 5},
%{"categoryId" => 1, "label" => "Condiments", "selectedAdd" => true,
"selectedSearch" => false, "value" => 6},
%{"categoryId" => 1, "label" => "Beverages", "selectedAdd" => true,
"selectedSearch" => false, "value" => 7}],
"description" => "Yummy chocolate bits for baking", "image" => "no-image",
"name" => "Chocolate Bits"}
20:49:46.103 [error] #PID<0.340.0> running Api.Router terminated
Server: 192.168.20.3:4000 (http)
Request: POST /products
** (exit) an exception was raised:
** (UndefinedFunctionError) function :ok.errors/0 is undefined (module :ok is not available)
:ok.errors()
(api) lib/api/repo.ex:33: Api.Repo.insertProductCategories/3
(api) lib/api/router.ex:1: Api.Router.plug_builder_call/2
(api) lib/plug/debugger.ex:123: Api.Router.call/2
(plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
(cowboy) /Users/Ben/Development/Projects/vepo/api/deps/cowboy/src/cowboy_protocol.erl:442: :cowboy_protoco
l.execute/4
I have only ever done this for a database insertion of more than 1 row but it doesn't have any validation:
Enum.each(subcategories, fn (subcategory) -> insert(subcategory) end)
And I have only used a changeset with validation for a one row insertion:
def insertProduct(conn, product) do
changeset = Api.Product.changeset(%Api.Product{}, product)
errors = changeset.errors
valid = changeset.valid?
case insert(changeset) do
{:ok, product} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Poison.encode!(%{
successs: product
}))
{:error, changeset} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(500, Poison.encode!(%{
failure: changeset
}))
end
end
I'm trying to merge these techniques. I would like to keep the validation code in there (eg the case for :ok and :error but am unsure how to do that when I am inserting more than one row into the database. What am I doing wrong?
You can use Ecto.Multi to sequence insertions for a bunch of changesets, and then run it in a transaction. The transaction will make sure that if there's any error in any insertion, the rest of the changes get rolled back.
multi = Enum.reduce(Enum.with_index(product["categories"]), Ecto.Multi.new, fn {productCategory, index}, multi ->
changeset = Api.ProductCategory.changeset(%Api.ProductCategory{c_id: productCategory["value"], p_id: productId})
Ecto.Multi.insert(multi, index, changeset)
end)
case Repo.transaction(multi) do
{:ok, categories} ->
# categories here is a map with the index as key and struct as value
...
{:error, failed_operation, failed_value, changes_so_far} ->
...
end
You can read more about the values returned by Repo.transaction for Ecto.Multi in this example in the documentation and the documentation of Ecto.Repo.transaction/2.
I think you need Enum.map in repo.ex, not Enum.each.
From the docs:
each(enumerable, fun)
each(t, (element -> any)) :: :ok
Invokes the given fun for each item in the enumerable.
Returns :ok.
Which is why you're seeing the function :ok.errors/0 is undefined

Is there a standard functional name for this function?

I'm sure this function is likely common ( or possibly achievable other ways ) but I'm not sure of what it'd be called. I'm thinking of a sliding window of a certain size :-
let slidingMap = (arr,size, f) => {
r = []
arr.reduce((acc, n) => {
let b = acc.concat(n);
if(b.length > size) {
b.shift();
}
if(b.length == size) {
r.push(f(b))
}
return b;
},[])
return r;
}
so given slidingMap([1,2,3,4,5,6], 2, b => b)
you'd get [ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ], [ 5, 6 ] ]
and slidingMap([1,2,3,4,5,6], 3, b => b)
you'd get [ [ 1, 2, 3 ], [ 2, 3, 4 ], [ 3, 4, 5 ], [ 4, 5, 6 ] ]
or for calculating differences :-
slidingMap([1,2,3,7,5,6],2, b => b.reduceRight((a, n) => a? a-n : n))
you'd get [ 1, 1, 4, -2, 1 ]
or moving average :-
slidingMap([1,2,3,7,5,6],3, b => b.reduce((a, n) => a+n,0)/b.length)
you'd get [ 2, 4, 5, 6 ]
so, is there a commonly implemented function(s) that achieves this?
Update
Probablly better implemented as
let sliding = (arr,size) => {
r = []
arr.reduce((acc, n) => {
let b = acc.concat(n);
if(b.length > size) {
b.shift();
}
if(b.length == size) {
r.push(b)
}
return b;
},[])
return r;
}
then just use map
sliding([1,2,3,4,5],2).map(somefunc);
Or perhaps using zip and skip ( using lodash in this case )
let sliding = (arr, size) =>
_.zip(..._.range(size).map(i => arr.slice(i)))
.filter(a => !a.some(v => v == undefined))
only trick here is the zip will insert undefined when it has no match so they need to be filtered out.

Count/increment is not working in logstash

I filter some "status code" as output attribute from each line. like, code = 100, code = 100, code = 200, code = 200,code = 200,code = 300
Here, I'm trying to get the count of each type of status codes like 100 = 2, 200 = 3, 300 = 1
The config am working on looks like,
filter {
grok {
patterns_dir => ["./patterns"]
match => [ "message", ["%{CUSTOMPATTERN:code}" ]]
}
mutate {
remove_field => ["host", "path", "#version", "#timestamp"]
}
}
output {
statsd {
increment => "%{code}"
count => ["code", "%{code}"]
}
stdout {
codec => json
}
}
I know the statsd config is wrong, but couldn't fix it.

node_redis get zrange withscores

Does anybody know how can I get members with scores by node redis?
I tried something like this:
client.ZRANGE(key, 0, -1, withscores, function(err, replies) {
});
Thanks.
This code looks good. Check out the following link for retrieving what you want :
http://ricochen.wordpress.com/2012/02/28/example-sorted-set-functions-with-node-js-redis/
Added the code here from that link example in case it is ever removed.
var rc=require('redis').createClient();
var _=require('underscore');
rc.zincrby('myset', 1, 'usera');
rc.zincrby('myset', 5, 'userb');
rc.zincrby('myset', 3, 'userc');
rc.zrevrange('myset', 0, -1, 'withscores', function(err, members) {
// the resulting members would be something like
// ['userb', '5', 'userc', '3', 'usera', '1']
// use the following trick to convert to
// [ [ 'userb', '5' ], [ 'userc', '3' ], [ 'usera', '1' ] ]
// learned the trick from
// http://stackoverflow.com/questions/8566667/split-javascript-array-in-chunks-using-underscore-js
var lists=_.groupBy(members, function(a,b) {
return Math.floor(b/2);
});
console.log( _.toArray(lists) );
});
rc.quit();
Seems your code is right. The following is the syntax to get zrange.
without score:
redisClient.zrange(keyName,start,stop,function(err,result){
//result is array
// every index will give you member name
})
Ex :
redisClient.zrange("mySortedset",-1,-1,function(err,result){
//result is array
// every index will give you member name
})
with score:
redisClient.zrange(keyName,start,stop,'withscores',function(err,result){
//result is array
// here even index will hold member
// odd index will hold its score
})
Ex :
redisClient.zrange("mySortedset",-1,-1,'withscores',function(err,result){
//result is array
// here even index will hold member
// odd index will hold its score
})
I tried with the prior accepted answers but i could not get the result i want and later i tried with the following code and got appropriate result,
Original output:
[ 'player:522',
'19685',
'player:164',
'19594',
'player:807',
'19171',
'player:694',
'19165',
'player:905',
'19108',
'player:859',
'19087',
'player:432',
'18973',
'player:515',
'18831',
'player:163',
'18750',
'player:4',
'18552' ]
Expected output:
{
"player:522": "19685",
"player:164": "19594",
"player:807": "19171",
"player:694": "19165",
"player:905": "19108",
"player:859": "19087",
"player:432": "18973",
"player:515": "18831",
"player:163": "18750",
"player:4": "18552"
}
Solution:
redisClient.ZREVRANGE('daily', 1, 10, 'WITHSCORES', function(err, result) {
result = _.fromPairs(_.chunk(result, 2));
return res.status(200).json(result);
});
The right approach for versions ^2.0,
var args = [ key,to, from ];
redisClient.zrevrangebyscore(args,function(err,data){
Vanilla JS Solution
Redis call:
redisClient.zrange(keyName, start, stop, 'withscores', function(err, result) {
// result.reduce ... (See below)
}
Here is a Vanilla-JS solution that I came up with pretty quickly.
For me, personally, it does not make sense to import underscore or any other library to perform such an easy task:
result.reduce(function (a, c, i) {
var idx = i / 2 | 0;
if (i % 2) {
a[idx].score = c;
} else {
a[idx] = { id: c };
}
return a;
}, []);
Assuming this input:
['player1', 13, 'player2', 11, 'player4', 7, 'player3', 3, 'player5', 0]
This function yields:
[
{ id: 'player1', score: 13 },
{ id: 'player2', score: 11 },
{ id: 'player4', score: 7 },
{ id: 'player3', score: 3 },
{ id: 'player5', score: 0 }
]
Here is another one to transform the result into a two-dimensional array:
result.reduce(function (a, c, i) {
var idx = i / 2 | 0;
if (i % 2) {
a[idx].push(c);
} else {
a[idx] = [c];
}
return a;
}, []);
which produces the following array:
[
[ 'player1', 13 ],
[ 'player2', 11 ],
[ 'player4', 7 ],
[ 'player3', 3 ],
[ 'player5', 0 ]
]
One line lambda version:
result.reduce((a, c, i) => i % 2 ? (a[i / 2 | 0].data = c, a) : (a[i / 2 | 0] = { id: c }, a), []);
Vanilla JS reduce works well here.
const result = [
'player:522',
'19685',
'player:164',
'19594',
'player:807',
'19171',
'player:694',
'19165',
'player:905',
'19108',
'player:859',
'19087',
'player:432',
'18973',
'player:515',
'18831',
'player:163',
'18750',
'player:4',
'18552'
]
const map = result.reduce((map, k, i, res) => {
if (i % 2 !== 0) {
map[res[i - 1]] = Number(k);
}
return map;
}, {})
map is now:
{
'player:522': 19685,
'player:164': 19594,
'player:807': 19171,
'player:694': 19165,
'player:905': 19108,
'player:859': 19087,
'player:432': 18973,
'player:515': 18831,
'player:163': 18750.65468,
'player:4': 18552
}
var data = []
results.map((result, index) => {
if (index % 2 == 0) {
data.push(results[index] = { player: results[index], score: results[index+1] })
}
})
console.log(data)

Resources