How to convert Erlang sys.config into Elixir config.exs? - erlang

This is just probably easy one.
For example this sys.config into mix config.exs:
{gsms, [{interfaces,
[
{gsms_0705, 1, [{device,"/dev/tty.usbserial-FTF5DP2J"},
{bnumber, "<phone-number>"},{baud,19200},
{reopen_timeout, 5000}]}
{gsms_0705, 2, [{device,"/dev/tty.HUAWEIMobile-Pcui"},
{bnumber, "<phone-number>"},
{reopen_timeout, 5000}]}
{gsms_0705, 3, [{device, "/dev/tty.usbserial"},
{bnumber, "<phone-number>"},
{baud, 9600}]}
]}
]}

If you fix the syntax errors (missing commas) and add the mandatory trailing dot, you can even parse the Erlang term from Elixir and let the parser do the conversion for you:
erlang_term = '{gsms, [{interfaces, [
{gsms_0705, 1, [{device,"/dev/tty.usbserial-FTF5DP2J"},
{bnumber, "<phone-number>"},{baud,19200},
{reopen_timeout, 5000}]},
{gsms_0705, 2, [{device,"/dev/tty.HUAWEIMobile-Pcui"},
{bnumber, "<phone-number>"},
{reopen_timeout, 5000}]},
{gsms_0705, 3, [{device, "/dev/tty.usbserial"},
{bnumber, "<phone-number>"},
{baud, 9600}]}
]}
]}.'
{:ok, tokens, _} = :erl_scan.string(erlang_term)
{:ok, result} = :erl_parse.parse_term(tokens)
IO.inspect result, limit: :infinity
This prints:
{:gsms,
[interfaces: [{:gsms_0705, 1,
[device: '/dev/tty.usbserial-FTF5DP2J', bnumber: '<phone-number>',
baud: 19200, reopen_timeout: 5000]},
{:gsms_0705, 2,
[device: '/dev/tty.HUAWEIMobile-Pcui', bnumber: '<phone-number>',
reopen_timeout: 5000]},
{:gsms_0705, 3,
[device: '/dev/tty.usbserial', bnumber: '<phone-number>', baud: 9600]}]]}
The first part of the tuple is the application name :gsms, the other part of the tuple is proplist containing the actual options. You can leave off the surrounding brackets and reformat this a bit and you will get:
config :gsms, interfaces: [
{:gsms_0705, 1, [
device: '/dev/tty.usbserial-FTF5DP2J',
bnumber: '<phone-number>',
baud: 19200,
reopen_timeout: 5000]},
{:gsms_0705, 2, [
device: '/dev/tty.HUAWEIMobile-Pcui',
bnumber: '<phone-number>',
reopen_timeout: 5000]},
{:gsms_0705, 3, [
device: '/dev/tty.usbserial',
bnumber: '<phone-number>',
baud: 9600]}]

I think this would be it:
config :gsms, :interfaces, [
{:gsms_0705, 1, [device: '/dev/tty.usbserial-FTF5DP2J',
bnumber: '<phone-number>',
baud: 19200,
reopen_timeout: 5000]},
# and so on
]
where :gsms is the application you're configuring, :interfaces is the key you'll retrieve through Application.get_env/2 (Application.get_env(:gsms, :interfaces)) and the list of {:gsms_*, ...} tuples is the value at that key.

Related

Merge sub-array in Swift

I have an array and I want to merge it.
This is the array:
let numbers = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
I need a output like this:
let result = [ [ 1, 4 ], [ 2, 5 ], [ 3, 6 ] ]
let numbers = [[1,2,3], [4,5,6]]
let result = zip(numbers[0], numbers[1]).map { [$0.0, $0.1]}
print(result) // -> [[1, 4], [2, 5], [3, 6]]
If the array has more elements the following would work.
let numbers = [[1,2,3], [4,5,6], [7,8,9]]
var result : [[Int]] = []
for n in 0...numbers.first!.count-1{
result.append(numbers.compactMap { $0[n] })
}
print(result) // -> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
You can use the zip global function, which, given 2 sequences, returns a sequence of tuples and then obtain an array of tuples by using the proper init.
var xAxis = [1, 2, 3, 4]
var yAxis = [2, 3, 4, 5]
let pointsSequence = zip(xAxis, yAxis)
let chartPoints = Array(pointsSequence)
print(chartPoints)
And then you can access the tuples like this :
let point = chartPoints[0]
point.0 // This is the 1st element of the tuple
point.1 // This is the 2nd element of the tuple

Distribute items into containers in twos - Rails

I have a list of 10 items -- it is an array of hashes.
[{ id: 1, name: 'one'}, { id: 2, name: 'two' } .. { id: 10, name: 'ten' }]
I also have a random number of containers -- let's say 3, in this case. These containers are hashes with array values.
{ one: [], two: [], three: [] }
What I want to do, is iterate over the containers and drop 2 items at a time resulting in:
{
one: [{id:1}, {id:2}, {id:7}, {id:8}],
two: [{id:3}, {id:4}, {id:9}, {id:10}],
three: [{id:5}, {id:6}]
}
Also, if the item list is an odd number (11), the last item is still dropped into the next container.
{
one: [{id:1}, {id:2}, {id:7}, {id:8}],
two: [{id:3}, {id:4}, {id:9}, {id:10}],
three: [{id:5}, {id:6}, {id:11}]
}
note: the hashes are snipped here so it's easier to read.
My solution is something like this: (simplified)
x = 10
containers = { one: [], two: [], three: [] }
until x < 1 do
containers.each do |c|
c << 'x'
c << 'x'
end
x -= 2
end
puts containers
I'm trying to wrap my head around how I can achieve this but I can't seem to get it to work.
Round-robin pair distribution into three bins:
bins = 3
array = 10.times.map { |i| i + 1 }
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
array.
each_slice(2). # divide into pairs
group_by. # group into bins
with_index { |p, i| i % bins }. # round-robin style
values. # get rid of bin indices
each(&:flatten!) # join pairs in each bin
Completely different approach, stuffing bins in order:
base_size, bins_with_extra = (array.size / 2).divmod(bins)
pos = 0
bins.times.map { |i|
length = 2 * (base_size + (i < bins_with_extra ? 1 : 0)) # how much in this bin?
array[pos, length].tap { pos += length } # extract and advance
}
# => [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10]]
If you absolutely need to have this in a hash,
Hash[%i(one two three).zip(binned_array)]
# => {:one=>[1, 2, 7, 8], :two=>[3, 4, 9, 10], :three=>[5, 6]}
The lovely (but likely not as performant) solution hinted at by Stefan Pochmann:
bins.times.with_object(array.to_enum).map { |i, e|
Array.new(2 * (base_size + (i < bins_with_extra ? 1 : 0))) { e.next }
}
This is just to show a different approach (and I would probably not use this one myself).
Given an array of items and the containers hash:
items = (1..10).to_a
containers = { one: [], two: [], three: [] }
You could dup the array (in order not to modify the original one) and build an enumerator that cycles each_value in the hash:
array = items.dup
enum = containers.each_value.cycle
Using the above, you can shift 2 items off the array and push them to the next container until the array is emtpy?:
enum.next.push(*array.shift(2)) until array.empty?
Result:
containers
#=> {:one=>[1, 2, 7, 8], :two=>[3, 4, 9, 10], :three=>[5, 6]}
You can use Enumerable#each_slice to iterate over a range from 0 to 10 in 3s and then append to an array of arrays:
containers = [
[],
[],
[]
]
(1...10).each_slice(3) do |slice|
containers[0] << slice[0]
containers[1] << slice[1]
containers[2] << slice[2]
end
p containers
# [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

Merge hashes containing same key & value pair

arr1 = [
{entity_type: "Mac", entity_ids: [3], cascade_id: 2, location_id: 1},
{entity_type: "Mac", entity_ids: [2], cascade_id: 2, location_id: 1},
{entity_type: "Mac", entity_ids: [9], cascade_id: 4, location_id: 1},
{entity_type: "Mac", entity_ids: [10], cascade_id: 4, location_id: 1}
]
This is the part of data, that I get after some of my logical iterations.
My desired output here for this example is
[{entity_type: "Mac", entity_ids: [3,2], cascade_id: 2, location_id: 1},
{entity_type: "Mac", entity_ids: [9,10], cascade_id: 4, location_id: 1}]
I want to know how to merge hashes if it's one or two key-value pair are same, merging other key's values to an array.
-> This is one more instance
arr2 = [
{entity_type: "Sub", entity_ids: [7], mac_id: 5, cascade_id: 1, location_id: 1},
{entity_type: "Sub", entity_ids: [10], mac_id: 5, cascade_id: 1, location_id: 1},
{entity_type: "Sub", entity_ids: [4], mac_id: 2, cascade_id: 1, location_id: 1},
{entity_type: "Sub", entity_ids: [11], mac_id: 7, cascade_id: 2, location_id: 2}
]
desired output for this instance is
[{entity_type: "Sub", entity_ids: [7, 10], mac_id: 5, cascade_id: 1, location_id: 1},
{entity_type: "Sub", entity_ids: [4], mac_id: 2, cascade_id: 1, location_id: 1},
{entity_type: "Sub", entity_ids: [11], mac_id: 7, cascade_id: 2, location_id: 2}]
You can compute the desired result as follows.
def doit(arr)
arr.each_with_object({}) do |g,h|
h.update(g.reject { |k,_| k==:entity_ids }=>g) do |_,o,n|
o.merge(entity_ids: o[:entity_ids] + n[:entity_ids])
end
end.values
end
doit(arr1)
#=> [{:entity_type=>"Mac", :entity_ids=>[3, 2], :cascade_id=>2, :location_id=>1},
# {:entity_type=>"Mac", :entity_ids=>[9, 10], :cascade_id=>4, :location_id=>1}]
doit(arr2)
#=> [{:entity_type=>"Sub", :entity_ids=>[7, 10], :mac_id=>5, :cascade_id=>1,
# :location_id=>1},
# {:entity_type=>"Sub", :entity_ids=>[4], :mac_id=>2, :cascade_id=>1,
# :location_id=>1},
# {:entity_type=>"Sub", :entity_ids=>[11], :mac_id=>7, :cascade_id=>2,
# :location_id=>2}]
This uses the form of Hash#update (aka merge!) that employs a block to determine the values of keys that are present in both hashes being merged. See the doc for an explanation of the block variables k, o and n.
If doit's argument is arr1, the steps are as follows.
arr = arr1
e = arr.each_with_object({})
#=> #<Enumerator: [{:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2,
# :location_id=>1},
# {:entity_type=>"Mac", :entity_ids=>[2], :cascade_id=>2,
# :location_id=>1},
# {:entity_type=>"Mac", :entity_ids=>[9], :cascade_id=>4,
# :location_id=>1},
# {:entity_type=>"Mac", :entity_ids=>[10], :cascade_id=>4,
# :location_id=>1}
# ]:each_with_object({})>
The first element of the enumerator is passed to the block and values are assigned to the block variables.
g, h = e.next
#=> [{:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1}, {}]
g #=> {:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1}
h #=> {}
Compute the (only) key for the hash to be merged with h.
a = g.reject { |k,_| k==:entity_ids }
#=> {:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}
Perform the update operation.
h.update(a=>g)
#=> {{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}=>
# {:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1}}
This is the new value of h. As h (which was empty) did not have the key
{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}
the block was not used to determine the value of this key in the merged hash.
Now generate the next value of the enumerator e, pass it to the block, assign values to the block variables and perform the block calculation.
g, h = e.next
#=> [{:entity_type=>"Mac", :entity_ids=>[2], :cascade_id=>2, :location_id=>1},
# {{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}=>
# {:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1}}]
g #=> {:entity_type=>"Mac", :entity_ids=>[2], :cascade_id=>2, :location_id=>1}
h #=> {{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}=>
# {:entity_type=>"Mac", :entity_ids=>[3, 2], :cascade_id=>2, :location_id=>1}}
a = g.reject { |k,_| k==:entity_ids }
#=> {:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}
h.update(a=>g) do |_,o,n|
puts "_=#{_}, o=#{o}, n=#{n}"
o.merge(entity_ids: o[:entity_ids] + n[:entity_ids])
end
#=> {{:entity_type=>"Mac", :cascade_id=>2, :location_id=>1}=>
# {:entity_type=>"Mac", :entity_ids=>[3, 2], :cascade_id=>2, :location_id=>1}}
This is the new value of h. As both g and h have the key a the block is consulted to obtain the value of that key in the merged hash (new h). The values of that block variables are printed.
_={:entity_type=>"Mac", :cascade_id=>2, :location_id=>1},
o={:entity_type=>"Mac", :entity_ids=>[3], :cascade_id=>2, :location_id=>1},
n={:entity_type=>"Mac", :entity_ids=>[2], :cascade_id=>2, :location_id=>1}
h[:entity_ids] is therefore replaced with
o[:entity_ids] + o[:entity_ids]
#=> [3] + [2] => [3, 2]
The calculations for the two remaining elements of e are similar, at which time
h #=> {{ :entity_type=>"Mac", :cascade_id=>2, :location_id=>1 }=>
# { :entity_type=>"Mac", :entity_ids=>[3, 2], :cascade_id=>2, :location_id=>1 },
# { :entity_type=>"Mac", :cascade_id=>4, :location_id=>1 }=>
# { :entity_type=>"Mac", :entity_ids=>[9, 10], :cascade_id=>4, :location_id=>1 }}
The final step is to return the values of this hash.
h.values
#=> <as shown above>
Note that some of the block variables are underscores (_). Though they are valid local variables, they are commonly used to indicate that they are not used in the block calculation. An alternative convention is to have the unused block variable begin with an underscore, such as _key.
This will work:
def combine(collection)
return [] if collection.empty?
grouping_key = collection.first.keys - [:entity_ids]
grouped_collection = collection.group_by do |element|
grouping_key.map { |key| [key, element[key]] }.to_h
end
grouped_collection.map do |key, elements|
key.merge(entity_ids: elements.map { |e| e[:entity_ids] }.flatten.uniq)
end
end
Here's what's going on:
First we determine a "grouping key" for the collection by sampling the keys of the first element and removing :entity_ids. All other keys combined make up the grouping key on which the combination depends.
The Enumerable#group_by method iterates over a collection and groups it by the grouping key we just constructed.
We then iterate over the grouped collection and merge in a new entity_ids attribute made up of the combined entity ids from each group.
There are two separate challanges in your problem.
merging the hashes.
merging only if other values are not matching.
Problem 1:
To get any custom behaviour while merging you can pass a block to merge method. In your case you want to combine arrays for entity ids. This blocks takes key and left and right values. In your scenerio you want to combine arrays if key == :entity_ids.
one_entity.merge(other_entity){ |key, left, right|
key== :entity_ids ? left + right : left
}
Problem 2:
To merge entities based on whether they have different attributes or same, i am using group_by. This will give me a hash combining entities that can be merged into array that i can map over and merge.
actual.group_by {|x| [x[:entity_type], x[:mac_id], x[:location_id]]}
Combining the two will give me the whole solution which works. You can add more attributes in group_by block if you want.
actual.group_by {|x| [x[:entity_type], x[:mac_id], x[:location_id]]}
.map{|_, entities| entities.reduce({}) { |result, entity|
result.merge(entity){|key, left, right|
key== :entity_ids ? left + right : left
}
}
}

Elixir: Split list into odd and even elements as two items in tuple

I am quiet new to Elixir programming and stuck badly at splitting into two elements tuple.
Given a list of integers, return a two element tuple. The first element is a list of the even numbers from the list. The second is a list of the odd numbers.
Input : [ 1, 2, 3, 4, 5 ]
Output { [ 2, 4], [ 1, 3, 5 ] }
I have reached to identify the odd or even but not sure how do I proceed.
defmodule OddOrEven do
import Integer
def task(list) do
Enum.reduce(list, [], fn(x, acc) ->
case Integer.is_odd(x) do
:true -> # how do I get this odd value listed as a tuple element
:false -> # how do I get this even value listed as a tuple element
end
#IO.puts(x)
end
)
end
You can use Enum.partition/2:
iex(1)> require Integer
iex(2)> [1, 2, 3, 4, 5] |> Enum.partition(&Integer.is_even/1)
{[2, 4], [1, 3, 5]}
If you really want to use Enum.reduce/2, you can do this:
iex(3)> {evens, odds} = [1, 2, 3, 4, 5] |> Enum.reduce({[], []}, fn n, {evens, odds} ->
...(3)> if Integer.is_even(n), do: {[n | evens], odds}, else: {evens, [n | odds]}
...(3)> end)
{[4, 2], [5, 3, 1]}
iex(4)> {Enum.reverse(evens), Enum.reverse(odds)}
{[2, 4], [1, 3, 5]}
Or you can use the Erlang :lists module:
iex> :lists.partition(fn (n) -> rem(n, 2) == 1 end, [1,2,3,4,5])
{[1,3,5],[2,4]}

A more balanced array manipulation than each_slice?

I have an array of 10 items and I want to split it up into 3 sections that look like this:
[1, 2, 3, 4]
[5, 6, 7]
[8, 9, 10]
Using each_slice I can get close:
a = *(1..10)
a.each_slice(4) # use 4 so I can fit everything into 3 sections
[1, 2, 3, 4]
[5, 6, 7, 8]
[9, 10]
But I want the first format which is more evenly distributed. I can do it writing my own method. But is there a built in way to do this in ruby 1.9+?
Update:
Since there's no built in way I'd like to change my question to - how would you implement it?
Here's my implementation
def chunk(a, pieces)
size = a.size / pieces
extra = a.size % pieces
chunks = []
start = 0
1.upto(pieces) do |i|
last = (i <= extra) ? size.next : size
chunks << a.slice(start, last)
start = chunks.flatten.size
end
chunks
end
call it like so
a = *(1..10)
puts chunk(a, 3)
will output
[1, 2, 3, 4]
[5, 6, 7]
[8, 9, 10]
If piece size is too big it pads with empty arrays
a = *(1..10)
puts chunk(a, 14)
will output
[[1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [], [], [], []]

Resources