Unexpected empty list from Erlang list-comprehension - erlang

I use list comprehension for transforming database rows from the list of tuples to the list of maps. One day I have added some new column into my database table and forget to change the code everywhere.
And because of that I discovered a strange effect: database rows become an empty list.
Example of code in erl console:
> DbRows = [{1, 1, 1}, {2, 2, 2}].
[{1,1,1},{2,2,2}]
> [#{<<"col1">> => Col1, <<"col2">> => Col2} ||{Col1, Col2} <- DbRows].
[]
Why Erlang does not generate exception error: no match of right hand side value in this case?
Is this code OK, or some other syntax is preferred for performing such data transforming?

Erlang does not generate any exception because that's a right syntax. Generator {Col1, Col2} <- DbRows is a filter in the same time. So any element that does not match pattern just skipped.
In your case I would do something like that:
-define(FIELDS, [id, some1, some2]).
DbRows = [{1, 1, 1}, {2, 2, 2}].
Prepare = fun(X) ->
maps:from_list(lists:zip(?FIELDS, tuple_to_list(X)))
end.
[ Prepare(Row) || Row <- DbRows].
And when you add new field you need to add that field in the macros.

I don't like this "feature", since in my experience it tends to mask bugs, but nikit's answer is correct about the reason for the result you see.
You can get the exception by moving the pattern matching to the left side of the list comprehension:
[ case Row of {Col1, Col2} -> #{<<"col1">> => Col1, <<"col2">> => Col2} || Row <- DbRows ]

Related

Remove duplicate tables in a nested table. Lua

Before you mark this question as duplicate of this please read the whole thing:
I have a table in Lua which is a table of tables something like the below one and I want to remove all the duplicate tables in it.
table1 = {{1, 2, 3}, {1, 2, 3}, {1, 2, 3, 4}}
What I want to do is to remove the duplicate tables and use only one. The result should be like the one below
table2 = {{1, 2, 3}, {1, 2, 3, 4}}
I tried a lot of methods I found online and some of my own and I couldn't get to do it.
Here is what I tried last
local test = {1,2,4,2,3,4,2,3,4,"A", "B", "A"}
local hash = {}
local res = {}
for _,v in ipairs(test) do
if (not hash[v]) then
res[#res+1] = v -- you could print here instead of saving to result table if you wanted
hash[v] = true
end
end
-- Here the test is the input, and res is the output table without
-- any duplicates but this works only for values in it and not for
-- nested tables.
Please help me out.
Let's take pen and paper and think.
What do we have?
A table that contains multiple tables. According to your example those inner tables are sequences. So they only have consecutive integer keys starting from 1. The inner tables' elements are either strings or numbers.
You want to remove duplicate inner tables that is a table that has the same elements as another table before it.
So what do we have to do?
We need to go through our table and check each element and ask if we have seen this inner table's contents before.
So we make a list of sequences that we have seen before.
1) Check if the next element is already on the list.
2) If it is on the list, remove it, else put it on the list.
3) back to 1
Now translate that into Lua
local table1 = {{1, 2, 3}, {1, 2, 3}, {1, 2, 3, 4}}
-- make a list to add what we already have seen
local list = {}
-- make a list of unique items as it is easier than removing items from our input list
local results = {}
-- go over the list and check each element
for i, innerTable in ipairs(table1) do
-- convert it to a string representation so we add it to our list easily
local serialized = table.concat(innerTable, "\x1f")
-- only if it is not on our list yet
if not list[serialized] then
-- add it to the list
table.insert(results, innerTable)
-- add the item to the result list
list[serialized] = true
end
end
-- print the results
for i,v in ipairs(results) do print(v) end

JSONB query in Rails for a key that contains an array of hashes

I have a Rails 5 project with a Page model that has a JSONB column content. So the structure looks like this (reduced to the bare minimum for the question):
#<Page id: 46, content: {..., "media" => [{ "resource_id" => 143, "other_key" => "value", ...}, ...], ...}>
How would I write a query to find all pages that have a resource_id of some desired number under the media key of the content JSONB column? This was an attempt that I made which doesn't work (I think because there are other key/value pairs in each item of the array):
Page.where("content -> 'media' #> ?", {resource_id: '143'}.to_json)
EDIT: This works, but will only check the first hash in the media array: Page.where("content -> 'media' -> 0 ->> 'resource_id' = ?", '143')
Using sql, this should give you all pages which have resource id 143:
select * from pages p where '{"resource_id": 143}' <# ANY ( ARRAY(select jsonb_array_elements ( content -> 'media' ) from pages where id=p.id ) );
Postgresql has a function called ANY (postgres docs) which uses the form expression operator ANY (array). The left-hand expression is evaluated and compared to each element of the array using the given operator.
Since the right hand side parameter to ANY has to be an array (not a json array), we use the jsonb_array_elements method to convert the content->media json array into a set of rows which are then converted into an array by using ARRAY().
The <# operator checks if the expression on the right contains the expression on the left side. Ex: '{"a": 1}'::jsonb <# '{"b": 2, "a": 1}'::jsonb will return true.

Power Query - split column by variable field lengths - account for null values

This other Power Query question and answer provides a solution to split character delimited text file into columns based on column character count widths.
But it doesn't account for nulls. When null value is encountered it gives an error at one of the columns to right. I can't quite say exactly what is happening. The error is
An error occurred in the ‘SplitText’ query. Expression.Error: The 'count' argument is out of range.
THe code for split function is:
let
SplitText = (text, lengths) =>
let
LengthsCount = List.Count(lengths),
// Keep track of the index in the lengths list and the position in the text to take the next characters from. Use this information to get the next segment and put it into a list.
Split = List.Generate(() => {0, 0}, each _{0} < LengthsCount, each {_{0} + 1, _{1} + lengths{_{0}}}, each Text.Range(text, _{1}, lengths{_{0}}))
in
Split,
// Convert the list to a record to
ListToRecord = (text, lengths) =>
let
List = SplitText(text, lengths),
Record = Record.FromList(List, List.Transform({1 .. List.Count(List)}, each Number.ToText(_)))
in
Record
in
ListToRecord
Then, in your table, add a custom column that uses this formula:
each SplitText([Column1], {4, 2, 5, 3})
I am using Excel 2010 64 bit and Power Query Version: 2.29.4217.1861
How to modify this to account for nulls?
Split = List.Generate(() => {0, 0}, each _{0} < LengthsCount, each {_{0} + 1, _{1} + lengths{_{0}}}, each try Text.Range(text, _{1}, lengths{_{0}}) otherwise null)

groovy simple way to find hole in list?

I'm using the grails findAllBy() method to return a list of Position(s). Position has an integer field called location, which ranges from 1 to 15. I need to find the lowest location in the position list that is free.
For example, if there are positions at locations 1,2 and 4, then the algorithm should return 3. If locations 1 - 4 were filled, it would return 5.
Is there some simple groovy list/map functions to get the right number?
Thanks
If your list of positions were (limited to a mx of 5 for brevity):
def list = [ 1, 2, 4, 5 ]
And you know that you have a maximum of 5 of them, you can do:
(1..5).minus(list).min()
Which would give you 3
Just another option, because I originally thought he wanted to know the first unused slot in a list, say you had this:
def list = ['a', 'b', null, 'd', 'e', null, 'g']
You could easily find the first empty slot in the array by doing this:
def firstOpen = list.findIndexOf{ !it } // or it == null if you need to avoid groovy truth
Tim's way works, and is good for small ranges. If you've got the items sorted by location already, you can do it in O(n) by leveraging findResult
def firstMissing = 0
println list.findResult { (it.location != ++firstMissing) ? firstMissing : null }
prints 3.
If they're not sorted, you can either modify your db query to sort them, or add sort{it.location} in there.

How do I match two arrays with duplicates in Ruby?

I know this removes duplicates :
#email.distributions.map(&:zip_code) & CardSignup.all.map(&:zip_code)
But I want to do the same thing, where I find anything that matches, but it also shows me duplicates.
Any ideas?
I am trying to find the amount of people who signed up for a card that have a matching zip code to a zip code preference I placed.
Array#reject to the rescue, again! Like Array#map, it accepts blocks, allowing you to do something like this:
zip_codes = CardSignup.all.map(&:zip_code)
#email.distributions.reject{|o| !zip_codes.include?(o.zip_code)}
Oh, but of course, if you like finding more elegant ways, always consider the operators like you already did. & will return a new array with objects that are in both, | will join and remove duplicates.
ruby-1.9.2-p0 > [1,2] | [2,3]
=> [1, 2, 3]
ruby-1.9.2-p0 > [1,2] & [2,3]
=> [2]
Edit: as Tokland said in the comments, since this is applied on a Rails Model, you may want to consider doing it as a select. Like this -
zip_codes = CardSignup.all.map(&:zip_code)
#email.distributions.where('zip_code IN (?)', zip_codes)
Or, do it with an INNER JOIN. Doesn't look as pretty though.
#email.distributions.joins('INNER JOIN card_signups ON card_signups.zip_code = email_distributions.zip_code').all
(If the table for #email.distributions is email_distributions..)

Resources