Is there a way to apply multiple method by one liner in ruby? - ruby-on-rails

There is an array like this:
a = [1,2,3,4]
I want to get the return values of size and sum like this.
size = a.size
sum = a.sum
Is there a way to get both values by a one-liner like this?
size, sum = a.some_method(&:size, &:sum)

In Ruby, you can do multiple assignments in one line:
size, sum = a.size, a.sum
It doesn't make it more readable, though.

You could do this:
a = [1,2,3,4]
methods = [:size, :max, :min, :first, :last]
methods.map { |m| a.send m }
#=> [4, 4, 1, 1, 4]

Another possible solution:
size, sum = a.size, a.reduce { |a,b| a = a + b }

Previous answers are correct, but if OP was actually concerned about walking the array multiple times, then array.size does not walk the array, it merely returns the length, thus there is no saving from a oneliner in that regard.
On the other hand, if size was just an example and the question is more about making multiple operations on an array in one go, then try something like this:
arr = [1,2,3,4,5,6]
product,sum = arr.inject([1,0]){|sums,el| [sums[0]*el, sums[1]+el]}
# => [720, 21]
That is, inject the array with multiple initial values for the results and then calculate new value for every element.

Related

In Rails, how do I figure out if an array of objects contains specific attributes matching given values?

I'm using Ruby on Rails 5.0.1 with Ruby 2.4. I have an array of objects, stored in the array, "results." Each object has a numeric attribute
numeric_attr
I would like to know, given my array, how I can tell if I have exactly one object with a numeric attribute value of "1" and incrementing by one. Order is not important. So, for instance, if I have an array of three objects,
[MyObject(numeric_attr = 2), MyObject(numeric_attr = 1), MyObject(numeric_attr = 3)]
I want to know if I have exactly one object with numeric_attr = 1, another object with numeric_attr = 2, and another with numeric_attr = 3. So the above satisfies the condition. The below example does not
[MyObject(numeric_attr = 4), MyObject(numeric_attr = 1), MyObject(numeric_attr = 3)]
because although there is an object with numeric_attr = 1, there is no object with numeric_attr = 2. It is possible thet the numeric_attr field is nil. How can I figure this out?
This one-liner should work:
results.map(&:numeric_attr).sort == (1..results.count).to_a
Explanation:
results
#=> [#<MyObject:... #attr=2>, #<MyObject:... #attr=3>, #<MyObject:... #attr=1>]
results.map(&:attr)
#=> [2, 3, 1]
results.map(&:attr).sort
#=> [1, 2, 3]
(1..results.length).to_a
#=> [1, 2, 3]
# therefore:
results.map(&:attr).sort == (1..results.count).to_a
#=> true
If there is a chance that numeric_attr is nil:
results.map(&:attr).compact.sort == (1..results.count).to_a
Of course, if there is even a single nil value, the result is guaranteed to be false.
If the sequence could start at any number, not just 1:
results.map(&:attr).sort == results.count.times.to_a.
map { |i| i + results.map(&:attr).sort.first }
This is not very efficient though, as it sorts the numbers twice.
If they always start at 1 #Máté's solution works, if they can start at any arbitrary number then you could:
count = 0
array_objects.sort_by(&:numeric_attr).each_cons(2) {|a,b| count+=1 if a.numeric_attr==b.numeric_attr-1 }
count+1==array_objects.count
Not as elegant but handles a lot more situations

Check if multiple (more than two) arrays match, regardless of element order in ruby

If I have two arrays a and b. I can compare them as a.uniq.sort == b.uniq.sort. This will tell me if the elements in a match those in b.
What if I have four arrays a, b, c, d? I need to make sure they are all equal to each other. What is the best way to do this?
[%w[a b c], %w[c b a], %w[a a b c], %w[b a b c]]
.group_by{|a| a.uniq.sort}.one? # => true
[%w[a b c], %w[c b a], %w[a a b c], %w[b a b c d]]
.group_by{|a| a.uniq.sort}.one? # => false
Take one array, sort it's unique elements and compare the other arrays with that as reference. Return false as soon as one is not equal.
ars = [a,b,c,d]
ref = ars.pop.uniq.sort #take care: pop mutates ars
p ars.all?{|ar| ar.uniq.sort == ref}
Here's a way that doesn't involve sorting.
Let arr be an array of the given arrays. If the arrays contained no duplicates, this would be simple:
first, *rest = arr
rest.all? { |a| (first-a).empty? && (a-first).empty? }
To deal with duplicates, we define a helper Array#difference:
class Array
def difference(other)
h = other.each_with_object(Hash.new(0)) { |e,h| h[e] += 1 }
reject { |e| h[e] > 0 && h[e] -= 1 }
end
end
In a nutshell, if n elements of an array a equal a given object and m elements of an array b equal the same object, the array a.difference(b) will contain (the first) [n-m,0].max elements of a that equal the object .
Then we can write the following.
first, *rest = arr
rest.all? { |a| first.difference(a).empty? && a.difference(first).empty? }
The block evaluates true if every element of a maps to a unique element of first, and vice-versa.
It may seem a waste of time to create Array#difference for just this one problem. I have found, however, it to be a valuable member of my tool kit, having wide application, so much so that I proposed it be added to the Ruby core. The link gives examples of its uses and also contains a link to an SO answer I gave that contains a longer list of problems where it has found application.
There's a nice reduce solution:
ars.reduce { |a, v| a.uniq.sort == v.uniq.sort ? v.uniq.sort : break }
Which ends up to pretty clean code used in conjunction with map:
ars.map { |v| v.uniq.sort }.reduce { |a, v| a == v ? v : break }
The map -> reduce version is not the the best for performance since all the array elements get uniq.sort'd, but it's readable and functional style.
Since you use uniq you can use intersection, without the uniq intersection couldn't be used to compare arrays.
arr.inject(:&).sort == arr.first.uniq.sort
This would mean that arrays like
[3,2,3,1]
[1,3,2]
[3,2,1]
[2,1,3,1]
would be the same
You can try:
a.uniq.sort == b.uniq.sort and b.uniq.sort == c.uniq.sort and c.uniq.sort == d.uniq.sort
To optimise it, you can store the result of b.uniq.sort and c.uniq.sort in some variable to prevent repeated computation before comparing.

Lua: Adding multiple rows to tables

Ok, so I'm looking to quickly generate a rather large table. Something that would look like this:
table{
{1, 1, 1, 1},
{1, 1, 1, 1},
{1, 1, 1, 1},
}
Only the table would contain far more rows, and far more values in those rows. I know using table.insert() I can easily add however many I need to a single row, but is there anyway I can also add whole new rows without typing it all out?
Use a for loop.
t = { }
for i = 1,100 do
table.insert(t, i) -- insert numbers from 1 to 100 into t
end
2D arrays are also very simple
t = { }
for row = 1,20 do
table.insert(t, { }) -- insert new row
for column = 1,20 do
table.insert(t[row], "your value here")
end
end
You could remember current row as in local current_row = t[row], but don't try these things to improve performance until you profile! Use them merely for readability, if you think it clearer expresses the purpose.
Also note that (and it's especially funky in 5.1 and newer with the #) you can just directly assing values to nonexisting indices, and they will be added.
You don't need to use table.insert:
t = {}
for row = 1,20 do
t[row] = {}
for column = 1,20 do
t[row][column]= "your value here"
end
end

Ruby array, convert to two arrays in my case

I have an array of string which contains the "firstname.lastname?some.xx" format strings:
customers = ["aaa.bbb?q21.dd", "ccc.ddd?ew3.yt", "www.uuu?nbg.xcv", ...]
Now, I would like to use this array to produce two arrays, with:
the element of the 1st array has only the string before "?" and replace the "." to a space.
the element of the 2nd array is the string after "?" and include "?"
That's I want to produce the following two arrays from the customers array:
1st_arr = ["aaa bbb", "ccc ddd", "www uuu", ...]
2nd_arr = ["?q21.dd", "?ew3.yt", "?nbg.xcv", ...]
What is the most efficient way to do it if I use customers array as an argument of a method?
def produce_two_arr customers
#What is the most efficient way to produce the two arrays
#What I did:
1st_arr = Array.new
2nd_arr = Array.new
customers.each do |el|
1st_Str, 2nd_Str=el.split('?')
1st_arr << 1st_str.gsub(/\./, " ")
2nd_arr << "?"+2nd_str
end
p 1st_arr
p 2nd_arr
end
Functional approach: when you are generating results inside a loop but you want them to be split in different arrays, Array#transpose comes handy:
ary1, ary2 = customers.map do |customer|
a, b = customer.split("?", 2)
[a.gsub(".", " "), "?" + b]
end.transpose
Anytime you're building an array from another, reduce (a.k.a. inject) is a great help:
But sometimes, a good ol' map is all you need (in this case, either one works because you're building an array of the same size):
a, b = customers.map do |customer|
a, b = customer.split('?')
[a.tr('.', ' '), "?#{b}"]
end.transpose
This is very efficient since you're only iterating through customers a single time and you are making efficient use of memory by not creating lots of extraneous strings and arrays through the + method.
Array#collect is good for this type of thing:
arr1 = customers.collect{ |c| c.split("?").first.sub( ".", "" ) }
arr2 = customers.collect{ |c| "?" + c.split("?").last }
But, you have to do the initial c.split("?") twice. So, it's effecient from an amount of code point of view, but more CPU intensive.
1st_arr = customers.collect{ |name| name.gsub(/\?.*\z/,'').gsub(/\./,' ') }
2nd_arr = customers.collect{ |name| name.match(/\?.*\z/)[0] }
array1, array2 = customers.map{|el| el.sub('.', ' ').split /(?:\?)/}.transpose
Based on #Tokland 's code, but it avoids the extra variables (by using 'sub' instead of 'gsub') and the re-attaching of '?' (by using a non-capturing regex).

Nested tables and numerical keys in Lua

I'm not sure if this is possible due to the numerical indices, but hopefully someone can point me in the right direction.
Given the table of:
t = { 13, 200, 12, 15, 23 }
how can I nest a table using the numbers?
t["200"] = {"stuff", "more stuff", "even more stuff"}
doesn't seem to work, as it'll create a position 200 and fill in the empty cells with null. I'd add a letter as a suffix/prefix, but the problem comes trying to sort the table numerically. Is this even possible, or am I stuck with a different method? Thanks!
Slight edit due to a realisation:
t["200"] = {"stuff", "more stuff", "even more stuff"}
actually creates a key of "200", whereas:
t[200] = {"stuff", "more stuff", "even more stuff"}
creates the index 200 with everything else null.
First, DeadMG is correct; you used a string rather than a numerical index. However, even if you did use a number index, it wouldn't help.
If you do this:
someTable = {"value1", "value2", {"value3a", "value3b"}};
someTable[50] = {"value50a", "value50b"};
The length of the table, #someTable, will still be 3. Why? Because Lua defines arrays in a table based on contiguous elements. Remember: you can access any element of any table; they are all conceptually filled with nil until you give them an actual value.
Lua defines length for a table as the number of values in a table if you start counting from numerical index 1 until you reach the first nil value. Since someTable[4] is nil, the length is 3.
If you want to insert a new element at the end of an array table, then you can do this:
someTable[#someTable + 1] = "newValue";
The value can itself be a table:
someTable[#someTable + 1] = {"newValuea", "newValueb"};
If you're just asking how to access a nested table, that's simple, and it has nothing to do with the keys you use.
There is nothing special about nested tables. Tables are values, and table entries can be any value, including other tables.
If you have a table, and want to walk the array entries in it, you use this:
local aTable = {"first", "second", "third", ...}
for i, value in ipairs(aTable) do
--`value` contains the entries in the table.
end
A nested table is no different; it is simply a matter of getting the table.
local nestedTable = { "first", "second", "third", ...}
nestedTable[#nestedTable + 1] = {"newFirst", "newSecond", ...}
local aTable = nestedTable[#nestedTable];
for i, value in ipairs(aTable) do
--`value` contains the entries in the table.
end
Or you could just do ipairs(nestedTable[#nestedTable]). Note that the particular key used here (an integer value) is entirely unimportant. That key could have been a string, a floating-point number, another table, some user-data, etc. It doesn't matter.
Note also that we use ipairs because we only want to iterate over the array members of the table. The length of the array is defined above. If we wanted to loop over every member of the table, we would use pairs instead of ipairs. Of course, pairs does an unordered search, so it is not guaranteed to be in array order.
If you want to recursively find every element in a nested table, you can do this:
local function RecursiveSearch(aTable)
for key, value in pairs(aTable) do --unordered search
if(type(value) == "table") then
RecursiveSearch(value)
else
--Do something with this.
end
end
end
Note that the above can do an infinite loop, since it is possible for a table to have circular references:
local tableA = {}
local tableB = {tableA}
local tableA[1] = tableB
RecursiveSearch(tableA) --Infinite loop.
Perhaps it helps to view your assignment like this:
t = { [1] = 13, [2] = 200, [3] = 12, [4] = 15, [5] = 23 }
To change what is currently 200 (namely t[2]), you do:
t[2] = {"stuff", "more stuff", "even more stuff"}
Edit: that results in your table looking like this:
t = { [1] = 13, [2] = {"stuff", "more stuff", "even more stuff"}, [3] = 12, [4] = 15, [5] = 23 }
-- or, equivalent::
t = { 13, {"stuff", "more stuff", "even more stuff"}, 12, 15, 23 }
The trouble is your use of "". Your table t contains a bunch of numbers, and you're entering a string as the key. You want to iterate over the table and do... something that you didn't particularly well define. However, you can't add to a table whilst iterating over it, so you might have to do some funny stuff.
t = { 13, 200, 12, 15, 23 }
newt = {};
for key, value in pairs(t) {
newt[value] = { };
}
This will create a table entry in newt, where the key is a value in the table t, for all values in t.

Resources