I'm trying to minimize procedural code in Ruby. I have the following function build_array. It procedurally builds up a array in a pair of nested each loops.
def build_array(max)
array = []
(1..max).each do |size|
(0..size).each do |n|
array << [n, size - n]
end
end
array
end
The function works correctly and produces output like this:
> build_array 2
=> [[0, 1], [1, 0], [0, 2], [1, 1], [2, 0]]
> build_array 3
=> [[0, 1], [1, 0], [0, 2], [1, 1], [2, 0], [0, 3], [1, 2], [2, 1], [3, 0]]
Can this function be further optimized with a reduce somehow? The building up of a new object based on the result of a series of operations seems to fit that pattern, but I can't figure it out. The order of the subarrays is not important.
Thanks!
Can this function be further optimized with a reduce somehow?
I don't know about "optimized", but yes, you definitely can use reduce. And I didn't even read your code before I answered!
There is an interesting reason why I was able to give that answer without even looking at your question, and that is that reduce is a general iteration operator. Now, what do I mean by that?
If you think about what reduce does, on a deep level, here is how it works. A collection can either be empty or not empty. Those are the only two cases. And the reduce operator takes three arguments:
The collection being reduced,
What to do when the collection is not empty, and
What to do when the collection is empty.
Now, you might say "three arguments, I only see one", but that's because we are using Ruby:
The first argument is the hidden self argument, i.e. the collection that you are calling reduce on,
The second argument is the block, and
The third argument is the actual argument that Ruby's Enumerable#inject takes.
Now, typically we think about, and call the third argument the "accumulator". But in some other languages, it is also called the "zero", and there you can more easily see the connection to processing an empty collection.
But think about it: when you iterator over a collection, you generally have to deal with the fact that the collection is empty. And you have to somehow produce the next step in case the collection is not empty. There is really nothing else you need to do when iterating.
But those two things are exactly what is being covered by the last two arguments of the reduce operator!
So, it turns out that reduce can do everything that iteration can do. Really, everything. Everything that can be done with for / in or each in Ruby, foreach in C#, for / in, for / of, and forEach in JavaScript, the enhanced for loop in Java and C++, etc. can be done with reduce.
That means that every method that exists in Enumerable can be done with reduce.
Another way to think about it, is that a collection is a program written in a language that has only two commands: ELEMENT(x) and STOP. And reduce is an interpreter for that programming language that allows you to customize and provide your own implementations of ELEMENT(x) and STOP.
That is why the answer to the question "can this iteration thing be done with reduce" is always "Yes", regardless of what the "thing" that you're doing actually is.
So, back to your question, here is what a naive, blind, mechanical 1:1 translation of your code to reduce would look like:
def build_array(max)
(1..max).reduce([]) do |acc, size|
(0..size).reduce(acc) do |acc, n|
acc << [n, size - n]
end
end
end
Whenever you have a code pattern of the form:
Create some object
Iterate over a collection and add to that object in each iteration
Return the object
That is exactly reduce: the object that you are creating is the accumulator and your loop body is the reducing operation. You can see that here:
Note that in general reduce is considered to be part of functional programming, and we are actually mutating the accumulator here. It would be more "pure" to instead return a new accumulator in each iteration:
def build_array(max)
(1..max).reduce([]) do |acc, size|
(0..size).reduce(acc) do |acc, n|
acc + [[n, size - n]]
end
end
end
Alternatively, Ruby also has an "impure" version of reduce called each_with_object that is specifically designed for mutating the accumulator. In particular, reduce uses the return value of the block as the accumulator object for the next iteration, whereas each_with_object always passes the same object and just ignores the return value of the block:
def build_array(max)
(1..max).each_with_object([]) do |size, array|
(0..size).each_with_object(array) do |n, array|
array << [n, size - n]
end
end
end
However, note that just because everything can be expressed as a reduce operation, not everything should be expressed as a reduce operation. There are several more specific operations, and if possible, those should be used.
In particular, in our case we are actually mostly transforming values, and that's what map is for. Or, flat_map if you want to process a nested collection into a non-nested one:
def build_array(max)
(1..max).flat_map do |size|
(0..size).map do |n|
[n, size - n]
end
end
end
However, the most elegant solution in my opinion is the recursive one:
def build_array(max)
return [] if max.zero?
build_array(max.pred) + (0..max).map do |n|
[n, max - n]
end
end
However, note that this solution might be blow the stack for high values of max. Although that may or may not be a problem in your case because the result array also grows pretty large very quickly, so at the point where you run out of stack space, you pretty much also would run out of RAM, even with a non-recursive solution. On my laptop with YARV, I was able to determine that it breaks somewhere between 10000 and 20000, at which point the array uses well over 1GB.
The solution for this is to use a lazy infinite stream that generates each element one-by-one as it is consumed:
build_array = Enumerator.new do |y|
(1..).each do |size|
(0..size).each do |n|
y << [n, size - n]
end
end
end
loop do
print build_array.next
end
# [0, 1][1, 0][0, 2][1, 1][2, 0][0, 3][1, 2][2, 1][3, 0][0, 4][1, 3][2, 2]
# [3, 1][4, 0][0, 5][1, 4][2, 3][3, 2][4, 1][5, 0][0, 6][1, 5][2, 4][3, 3]
# [4, 2][5, 1][6, 0][0, 7][1, 6][2, 5][3, 4][4, 3][5, 2][6, 1][7, 0][0, 8]
# [1, 7][2, 6][3, 5][4, 4][5, 3][6, 2][7, 1][8, 0][0, 9][1, 8][2, 7][3, 6]
# [4, 5][5, 4][6, 3][7, 2][8, 1][9, 0][0, 10][1, 9][2, 8][3, 7][4, 6][5, 5]
# [6, 4][7, 3][8, 2][9, 1][10, 0][0, 11][1, 10][2, 9][3, 8][4, 7][5, 6][6, 5]
# [7, 4][8, 3][9, 2][10, 1][11, 0][0, 12][1, 11][2, 10][3, 9][4, 8][5, 7]
# [6, 6][7, 5][8, 4][9, 3][10, 2][11, 1][12, 0][0, 13][1, 12][2, 11]
# [3, 10][4, 9][5, 8][6, 7][7, 6][8, 5][9, 4][10, 3][11, 2][12, 1][13, 0]
# [0, 14][1, 13][2, 12][3, 11][4, 10][5, 9][6, 8][7, 7][8, 6][9, 5][10, 4]
# [11, 3][12, 2][13, 1][14, 0][0, 15][1, 14][2, 13][3, 12][4, 11][5, 10]
# [6, 9][7, 8][8, 7][9, 6][10, 5][11, 4][12, 3][13, 2][14, 1][15, 0]
# [0, 16][1, 15][2, 14][3, 13][4, 12][5, 11][6, 10][7, 9][8, 8][9, 7][10, 6]
# [11, 5][12, 4][13, 3][14, 2][15, 1][16, 0][0, 17][1, 16][2, 15][3, 14]
# [4, 13][5, 12][6, 11][7, 10][8, 9][9, 8][10, 7][11, 6][12, 5][13, 4][14, 3]
# [15, 2][16, 1][17, 0][0, 18][1, 17][2, 16][3, 15][4, 14][5, 13][6, 12]
# [7, 11][8, 10][9, 9][10, 8] …
You can let this run forever, it will never use any more memory. On my laptop, the Ruby process never grows beyond 9MB, where it was using multiple GB previously.
You can implement this with map instead of each ... essentially the same code but you don't need to shovel it into an array as map returns an array for you.
def build_array(max)
(1..max).map{|s|(0..s).map{|n|[n,s-n]}}.flatten(1)
end
reduce or the alias inject would require you to still insert into an array, so I can't see it would be better than your original method. I'd only use reduce if I wanted to return an object that consolidates the input array into a smaller collection.
I think this question is mostly for Code Review
But let's try to help you:
Here is example with Enumerable#inject and Array#new
def new_build_array(max)
(1..max).inject([]) do |array, size|
array += Array.new(size+1) { |e| [e, size-e] }
end
end
> new_build_array 2
=> [[0, 1], [1, 0], [0, 2], [1, 1], [2, 0]]
> new_build_array 3
=> [[0, 1], [1, 0], [0, 2], [1, 1], [2, 0], [0, 3], [1, 2], [2, 1], [3, 0]]
I am stuck on this problem. I tried to solve it with Ruby.
My answer is
4.times{|i| puts "#{i}"} && 5.upon(7){|i| puts "#{i}}
but, I am not sure that it is correct or not.
This should work for you
result = (0..3).to_a.product((5..7).to_a)
puts result.inspect
#=> [[0, 5], [0, 6], [0, 7], [1, 5], [1, 6], [1, 7], [2, 5], [2, 6], [2, 7], [3, 5], [3, 6], [3, 7]]
not sure that it is correct or not
Generally, in such a case, you try to run the code. Had you done so, you would have noticed (incrementally) that you have a quote missing, that there is no such method as Fixnum#upon (upto would have done it), that the code only outputs a single digit at a time and never a pair, and that the second loop does not run (since times returns nil, and nil && x gets short-circuited so that x does not evaluate).
As to your question, it asks for the sequence of number pairs, and not for its display, so puts is not needed. Also, since you need pairs, you should have the two loops nested, not one after another.
(0..3).flat_map { |x| (4..7).map { |y| [x, y] } }
would be one easy way to write it.
EDIT: flat_map is cooler than map.flatten. Still, macek's answer is more specific +1.
I have a bunch of objects in an array and would like to sort by a value that each object has. The attribute in question in each object is a numeric value.
For example:
[[1, ..bunch of other stuff],[5, ""],[12, ""],[3, ""],]
would become:
[[1, ..bunch of other stuff],[3, ""],[5, ""],[12, ""],]
I want to sort by the numerical value stored in each of the objects.
[5, 3, 4, 1, 2] becomes [1, 2, 3, 4, 5], however these numbers are stored inside objects.
The other answers are good but not minimal. How about this?
lst.sort_by &:first
The sort method can take a block to use when comparing elements:
lst = [[1, 'foo'], [4, 'bar'], [2, 'qux']]
=> [[1, "foo"], [4, "bar"], [2, "qux"]]
srtd = lst.sort {|x,y| x[0] <=> y[0] }
=> [[1, "foo"], [2, "qux"], [4, "fbar"]]
Assuming that you want to sort only according to the first element,
[[1, ..bunch of other stuff],[5, ""],[12, ""],[3, ""],].
sort_by{|n, *args| n}
or
[[1, ..bunch of other stuff],[5, ""],[12, ""],[3, ""],].
sort_by{|n, args| n}
When sorting objects and complex structures use sort_by. Sort_by performs a "Schwartzian Transform" which can make a major difference in sort speed.
Because you didn't provide enough information to be usable I'll recommend you read the docs linked above. You'll find its very easy to implement and can make a big difference.
Let's say I have a class of 30 students and want generate every possible way in which they can be partitioned into groups of 5 (order is irrelevant).
I know how to find all the combinations of students to form one group individually (http://www.merriampark.com/comb.htm). By using that iterator and some recursion, I can find PERMUTATIONS of the possible group combinations. However, order in which the groups are selected isn't relevant and I'd like to minimize my execution time. So how do I find the unique COMBINATIONS of the possible groups?
The above algorithm uses lexicographical ordering to avoid generating duplicate combinations... is there a way that I can use that idea on groups instead of on objects?
I know Ruby well and Java/Python less well. Thanks in advance for any advice!
Well, there's (30C5*25C5*20C5*15C5*10C5*5C5)/6! = 30!/(6!*5!6) = 123,378,675,083,039,376 different partitons of 30 into groups of 5, so generating them all will take some time, no matter what method you use.
In general, though, a good method to selecting such a partition is to use some ordering on the elements, and find the grouping for the highest ungrouped element, and then group the rest.
find_partition = lambda do |elts|
if elts.empty?
[[]]
else
highest = elts.pop
elts.combination(4).map do |others|
find_partition[elts - others].map { |part| part << [highest,*others] }
end.inject(:+)
end
end
find_partition[(1..30).to_a]
This way you're only generating each partition once
This is an old question, but anyway, for the record, that's how I would it in Ruby:
class Array
def groups_of_size(n)
Enumerator.new do |yielder|
if self.empty?
yielder.yield([])
else
self.drop(1).combination(n-1).map { |vs| [self.first] + vs }.each do |values|
(self - values).groups_of_size(n).each do |group|
yielder.yield([values] + group)
end
end
end
end
end
end
I use an enumerator because the output can grow very quickly, a strict output (an array for example) wouldn't be useful. A usage example:
>> pp [0, 1, 2, 3, 4, 5].groups_of_size(3).to_a
=>
[[[0, 1, 2], [3, 4, 5]],
[[0, 1, 3], [2, 4, 5]],
[[0, 1, 4], [2, 3, 5]],
[[0, 1, 5], [2, 3, 4]],
[[0, 2, 3], [1, 4, 5]],
[[0, 2, 4], [1, 3, 5]],
[[0, 2, 5], [1, 3, 4]],
[[0, 3, 4], [1, 2, 5]],
[[0, 3, 5], [1, 2, 4]],
[[0, 4, 5], [1, 2, 3]]]
You could do some post-processing on the permutations. Some pseudo-code (implement in the language of your choice...):
// We have a list of lists called 'permutations'
// combinations is an (empty) list of lists
for each permutation in permutations
{
sortedPermutation = permutation.sort()
if (! combinations.find(sortedPermutation) )
{
combinations.add(sortedPermutation);
}
}
Probably not the most efficient; I'd add the sort & compare to the code that generates the permutations personally.
One possibility would be to find all combinations to form an individual group, then go through and generate combinations that don't contain members of that individual group. Something like:
List<List<Student>> combinations=Combinations(students);
public void GenerateCombinations(int startingIndex, List<List<Student>> currentGroups, int groupsLeft)
{
if(groupsLeft==0) ProcessCombination(currentGroups);
for(int i=startingIndex; i<combinations.Count; i++)
{
if combinations[i] does not contain a student in current groups
GenerateCombinations(i+1, currentGroups + combinations[i], groupsLeft -1);
}
}
It won't be the most efficient method to go about it, but it should generate all combinations of groups. I suspect better performance could be had if you were to generate temporary lists of combinations, where in all groups that can't occur were removed, but that would be a bit more complex.
As a slight aside, there should be 142,506 combinations of 30 students to form a single group of 5. My <sarcasm> awesome </sarcasm> math skills suggest that there should be about 10^17 = 100 quadrillion combinations of groups of students (30!/((5!^6)*6!); 30! orderings of students, ordering of 6 groups of 5 does not matter, and ordering of those 6 groups doesn't matter). You might be sitting there a while waiting for this to finish.