Search an two dimensional array by another array - ruby

I have two arrays. One Mapper and one with my ID's.
My Array with the external ID's:
genres_array = [12,28,16]
The Mapper Array (Internal-ID, External-ID)
mapper = [
[1,12],
[2,18],
[3,19],
[4,28],
[5,16],
[6,90],
]
As Result i would like to have now a new array, with only the internal values of the genres_array (the genres_array had the external values first). In this case the result would be [1,4,5]
I tried a lot of ways but i really have no idea how to solve this simple problem in a clean way. Im pretty sure it will be something like
genres_array.map { |genre_id| get_internal_id_from_mapper }
PS: It could also happen that a ID won't be found in the mapper. In that i case i just want to remove it from the array. Any idea?

You're looking for rassoc:
genres_array.map { |genre_id| mapper.rassoc(genre_id)[0] }
Which results in
[1, 4, 5]
EDIT: Just read the PS - try something like this:
genres_array.map { |genre_id|
subarr = mapper.rassoc genre_id
subarr[0] if subarr
}.compact
Then for an input of
genres_array = [12,28,100,16]
You would still get the output
[1, 4, 5]

Another way that won't throw an exception if the external id is not found:
genres_array = [12,28,16]
mapper = [
[1,12],
[2,18],
[3,19],
[4,28],
[5,16],
[6,90],
]
internal_ids = genres_array.map do |genre_id|
element = mapper.detect { |m| m[1] == genre_id }
element ? element[0] : nil
end.compact

Another solution involving a hash:
Hash[mapper].invert.values_at(*genres_array)
Asking for values that does not exist will return nil, if you do not want the nil just add .compact at the end.

Related

Finding the sum,highest,lowest in a Hash/Array. Ruby

I am fairly new to this so I apologize in advance of my newbieness. I have been working on a project that I want to get the sum, highest,lowest out of a hash/array. I have tried numerous times to get this right but I typically will get an error such as, fixNum cannot convert int to string and undefined method. I will attempt to fix these issues and then run into another issue so I am at a loss. For the record in my text file I have 1,Foo,22 2,Smith,30 my output looks like this {1=>["Foo",22], 2=>["Smith",30]} I would like the highest number to show 30, lowest to be 22 and total to be 52 for different outputs.
You can do as below suppose lets say a variable a = {a: [a,1],b: [b,1] } then
values = a.values.map(&:last) //Gives the last element of each array
max= a.max
min = a.min
sum = a.sum
Okay, this is very ugly and someone will probably improve upon it but it works. Assuming I understand the output you would like.
elements = h.map{ |element| element[1] }.map { |element| element[1]}
# sum
elements.sum
# highest
elements.max
# lowest
elements.min
https://repl.it/repls/AntiqueOldfashionedRom
Convert to hash and calculate min max based on values
data = "1,Foo,22 2,Smith,30"
people =
data.split(",")
.each_slice(3)
.map {|slice| [slice[0], [slice[1], slice[2]]] }
.to_h
values = people.values.map {|person| person[1] }
min = values.min
max = values.max
sum = values.sum

Ruby: deleting object while looping over list with that object

So I have multiple lists to keep track of objects in a 2D game, but if these objects go off screen I want to remove these objects so they are no longer updated. What I have below works for me, but this doesn't work in other languages. Usually I have to make another "destroy list" that saves the objects I want to destroy and then loop again to remove them, because you can't remove an object from the list while iterating without some visible glitch.
Is Ruby just not showing any visible glitch while doing this or does Ruby's array work differently when removing multiple possible objects from a list while it's still iterating?
objects = []
objects.each{|o| o.withinBounds ? o.update : objects.delete(o)}
In Ruby you will actually find a glitch if you do what you are saying.
Try this:
objects = [1,2,3,4]
objects.each { |el| objects.delete(el) }
=> [2, 4]
You would expect the result to be an empty array, but is not. We are messing up with the elements of the arr and each gets confused, because the length of the array has changed. The each iterator looks something like this in pseudocode:
count = 0
while length(objects) > count
yield objects[count]
count + 1
end
So, in the example I shown above, the reason why we get [2, 4] can be explained on a step by step analysis on what objects.each { |el| objects.delete(el) } is doing:
We start with 4 (length of objects) > 0.
Number 1 is yielded, and deleted.
count = 1
3 (length of objects) > 1
Number 3 is yielded and deleted.
count = 2
2 (length of objects) is not bigger than count
We are done, so we have [2, 4]
There is a better way to do what you are trying, by using delete_if:
new_objects = objects.delete_if {|o| o.withinBounds }

How can I translate an array using a hash?

What I'm trying to do is giving a list of columns, get an array of column formats. I have an array of column names, and a has where the key is the column name and the value is the format the column needs. If there's no value in the hash for a given column, it needs to be nil in the resulting array.
Given:
report_columns = ["val1", "val2", "subtotal",
"othertotal", "grand_total", "moar_total"]
column_formats = {"grand_total" => #highlight_money,
"subtotal" => #money}
I can easily do it with this code:
datatype_array = []
report_columns.each {|col| datatype_array << column_formats[col] }
# do stuff with datatype_array
But this is ruby. There's a more concise way to do this! Please let me know what magic method I'm missing.
You're first attempt should be a simple map rather than an each that accumulates onto an array.
datatype_array = report_columns.map { |c| column_formats[c] }
You can also splat the array of columns into Hash#values_at, which expects multiple key names as individual arguments:
datatype_array = column_formats.values_at(*report_columns)

Is there an equivalent to `Array::sample` for hashes?

I'm looking to extract n random key-value pairs from a hash.
Hash[original_hash.to_a.sample(n)]
For Ruby 2.1,
original_hash.to_a.sample(n).to_h
I don't know of such method. Still you can do something like:
h[h.keys.sample]
If you need to sample more than one element the code will have to be a bit more complicated.
EDIT: to get key value pairs instead of only the value you can do something like:
keys_sample = h.keys.sample(n)
keys_sample.zip(keys_sample.map{|k| h[k])
Reading the top ranked answers, I'd go with it depends:
If you want to sample only one element from the hash, #Ivaylo Strandjev's solution only relies on hash lookup and Array#sample:
hsh[hsh.keys.sample]
To sample multiple hash elements, #sawa's answer leverages Array#to_h:
hsh.to_a.sample(n).to_h
Note that, as #cadlac mentions, hsh.to_a.sample.to_h won't work as expected. It will raise
TypeError: wrong element type String at 0 (expected array)
because Array#sample in this case returns just the element array, and not the array containing the element array.
A workaround is his solution, providing an n = 1 as an argument:
hsh.to_a.sample(1).to_h
PS: not looking for upvotes, only adding it as an explanation for people newer to Ruby.
If your sample has only one element, you could use this:
sample = h.keys.sample
h.select { |k,v| k == sample }
Or if your sample contains more than one element, use this:
n = 2
sample = h.keys.sample(n)
h.select { |k,v| sample.include?(k) }
One way to accomplish this:
rank_hash = {"Listen" => 1, "Download" => 60, "Share" => 150, "Purchase" => 700 }
rank_array = rank_hash.to_a
Than call this to get random array sample of the k/v pair:
rank_array[rand(0..3)]
or this to not hard-code the arrays length:
rank_array[rand(0..(rank_array.length) -1)]
Example:
["Download", 60]

Associatively sorting a table by value in Lua

I have a key => value table I'd like to sort in Lua. The keys are all integers, but aren't consecutive (and have meaning). Lua's only sort function appears to be table.sort, which treats tables as simple arrays, discarding the original keys and their association with particular items. Instead, I'd essentially like to be able to use PHP's asort() function.
What I have:
items = {
[1004] = "foo",
[1234] = "bar",
[3188] = "baz",
[7007] = "quux",
}
What I want after the sort operation:
items = {
[1234] = "bar",
[3188] = "baz",
[1004] = "foo",
[7007] = "quux",
}
Any ideas?
Edit: Based on answers, I'm going to assume that it's simply an odd quirk of the particular embedded Lua interpreter I'm working with, but in all of my tests, pairs() always returns table items in the order in which they were added to the table. (i.e. the two above declarations would iterate differently).
Unfortunately, because that isn't normal behavior, it looks like I can't get what I need; Lua doesn't have the necessary tools built-in (of course) and the embedded environment is too limited for me to work around it.
Still, thanks for your help, all!
You seem to misunderstand something. What you have here is a associative array. Associative arrays have no explicit order on them, e.g. it's only the internal representation (usually sorted) that orders them.
In short -- in Lua, both of the arrays you posted are the same.
What you would want instead, is such a representation:
items = {
{1004, "foo"},
{1234, "bar"},
{3188, "baz"},
{7007, "quux"},
}
While you can't get them by index now (they are indexed 1, 2, 3, 4, but you can create another index array), you can sort them using table.sort.
A sorting function would be then:
function compare(a,b)
return a[1] < b[1]
end
table.sort(items, compare)
As Komel said, you're dealing with associative arrays, which have no guaranteed ordering.
If you want key ordering based on its associated value while also preserving associative array functionality, you can do something like this:
function getKeysSortedByValue(tbl, sortFunction)
local keys = {}
for key in pairs(tbl) do
table.insert(keys, key)
end
table.sort(keys, function(a, b)
return sortFunction(tbl[a], tbl[b])
end)
return keys
end
items = {
[1004] = "foo",
[1234] = "bar",
[3188] = "baz",
[7007] = "quux",
}
local sortedKeys = getKeysSortedByValue(items, function(a, b) return a < b end)
sortedKeys is {1234,3188,1004,7007}, and you can access your data like so:
for _, key in ipairs(sortedKeys) do
print(key, items[key])
end
result:
1234 bar
3188 baz
1004 foo
7007 quux
hmm, missed the part about not being able to control the iteration. there
But in lua there is usually always a way.
http://lua-users.org/wiki/OrderedAssociativeTable
Thats a start. Now you would need to replace the pairs() that the library uses. That could be a simples as pairs=my_pairs. You could then use the solution in the link above
PHP arrays are different from Lua tables.
A PHP array may have an ordered list of key-value pairs.
A Lua table always contains an unordered set of key-value pairs.
A Lua table acts as an array when a programmer chooses to use integers 1, 2, 3, ... as keys. The language syntax and standard library functions, like table.sort offer special support for tables with consecutive-integer keys.
So, if you want to emulate a PHP array, you'll have to represent it using list of key-value pairs, which is really a table of tables, but it's more helpful to think of it as a list of key-value pairs. Pass a custom "less-than" function to table.sort and you'll be all set.
N.B. Lua allows you to mix consecutive-integer keys with any other kinds of keys in the same table—and the representation is efficient. I use this feature sometimes, usually to tag an array with a few pieces of metadata.
Coming to this a few months later, with the same query. The recommended answer seemed to pinpoint the gap between what was required and how this looks in LUA, but it didn't get me what I was after exactly :- which was a Hash sorted by Key.
The first three functions on this page DID however : http://lua-users.org/wiki/SortedIteration
I did a brief bit of Lua coding a couple of years ago but I'm no longer fluent in it.
When faced with a similar problem, I copied my array to another array with keys and values reversed, then used sort on the new array.
I wasn't aware of a possibility to sort the array using the method Kornel Kisielewicz recommends.
The proposed compare function works but only if the values in the first column are unique.
Here is a bit enhanced compare function to ensure, if the values of a actual column equals, it takes values from next column to evaluate...
With {1234, "baam"} < {1234, "bar"} to be true the items the array containing "baam" will be inserted before the array containing the "bar".
local items = {
{1004, "foo"},
{1234, "bar"},
{1234, "baam"},
{3188, "baz"},
{7007, "quux"},
}
local function compare(a, b)
for inx = 1, #a do
-- print("A " .. inx .. " " .. a[inx])
-- print("B " .. inx .. " " .. b[inx])
if a[inx] == b[inx] and a[inx + 1] < b[inx + 1] then
return true
elseif a[inx] ~= b[inx] and a[inx] < b[inx] == true then
return true
else
return false
end
end
return false
end
table.sort(items,compare)

Resources