First time poster. I am looking at a nested array. My objective is to find the smallest value of each array then return the smallest values as a new array. I understand my issue is .sort isn't actually happening. Why isn't it sorting each iteration and how can I fix it?
edit: found my issue! I was sorting the array, but after that line the array goes back to normal. What I needed to do was assign a local variable for small.sort then add the first element of that variable to the array.
array = [
[3, 5, 7, 1, 2],
[98, 35, 2, 34],
[88, 37, 5, 6]
]
smallest_array = []
array.each do |small|
small.sort
smallest_array << small[0]
end
smallest_array
expected: [1, 2, 5]
got: [3, 98, 88]
The main problem is inside these 2 lines:
small.sort
smallest_array << small[0]
#sort is not change the small, but returns a new array,
so to fix it, you can do it in two ways:
1st way:
sorted_small = small.sort
smallest_array << sorted_small[0]
2nd way:
small.sort!
smallest_array << small[0]
Both ways will work, as in first case you assign new array returned from #sort to variable. In second #sort! will change small array.
P.S.
Some refactoring suggestion with #map and #min
smallest_array = array.map { |small| small.min }
Array#sort is non-destructive: it returns a new array that has the same elements as the original one but in sorted order. You never save it anywhere, so it is discarded. You could have used smallest_array << small.sort[0] and it would work.
Array#sort! is the destructive version. Your code would also work if you change small.sort to small.sort!, but it would also change your original array. Sometimes this is what you want, but such a side effect would probably not be appreciated if you just want to know the smallest elements in sub-arrays.
However, you could also use min instead of sort here. In fact the whole thing can be written much more compactly as
smallest_array = array.map(&:min)
Related
I was working on a Codewar problem and the method I came up with worked with small arrays, but didn't work for very large arrays.
The solution provided used the inject method, which I assume is more efficient than the combination of map and with_index I had.
However, I'm not sure I understand why the inject method is more efficient than looping. Could someone please shine some light on this?
The problem was the following:
Given an array, return an array where each element is the sum of the array's subparts.
Example: array = [0, 1, 3, 6, 10]
I'm summing every array element while iterating on the array (so the array gets smaller and smaller):
[0, 1, 3, 6, 10] => 20
[1, 3, 6, 10] => 20
[3, 6, 10] => 19
[6, 10] => 16
[10] => 10
[] => 0
Hence the method would return here: [20, 20, 19, 16, 10, 0]
My solution (which works for small arrays but is too inefficient for large arrays):
def parts_sums(ls)
(ls + [0]).map.with_index { |_, i| ls[i..-1].sum }
end
The provided solution:
def parts_sums(ls)
ls.reduce([ls.sum]) { |sums, x| sums << sums.last - x }
end
By browsing the Codewars comment section, I've understood that this performance difference is linked to the Big O notation. However, every resource I found on this topic is way beyond my mathematical and algorithm understanding.
I've also looked at the Ruby doc but I don't understand C enough to be able to read the implementation of those methods.
Could someone please explain in quite simple terms why inject/reduce is more efficient than map.with_index in this situation?
Let’s start with reduce version.
ls.reduce([ls.sum]) { |sums, x| sums << sums.last - x }
It sums an array once (on step zero,) and then it subtracts two integers on each subsequent iteration.
Your solution sums the tail of the array on each step. Summing requires the whole array traversal, that’s why it is quite inefficient on large arrays.
I am trying to print all the different sums of all combinations in this array [1,2,3]. I want to first push every sum result to a new array b, then print them using b.uniq so that non of the sum results are repeated.
However, with the code I have, the 3 repeats itself, and I think it is because of the way it is pushed into the array b.
Is there a better way of doing this?
a = [1,2,3]
b = []
b.push a
b.push a.combination(2).collect {|a,b| (a+b)}
b.push a.combination(3).collect {|a,b,c| (a+b+c)}
puts b.uniq
p b #[[1, 2, 3], [3, 4, 5], [6]]
Can someone please help me with this? I am still new in ruby.
Because an Array of arbitrary length can be summed using inject(:+), we can create a more general solution by iterating over the range 1..n, where n is the length of the Array.
(1..(a.size)).flat_map do |n|
a.combination(n).map { |c| c.inject(&:+) }
end.uniq
#=> [1, 2, 3, 4, 5, 6]
By using flat_map, we can avoid getting the nested Array result, and can call uniq directly on it. Another option to ensure uniqueness would be to pass the result to a Set, for which Ruby guarantees uniqueness internally.
require "set"
sums = (1..(a.size)).flat_map do |n|
a.combination(n).map { |c| c.inject(&:+) }
end
Set.new(sums)
#=> #<Set: {1, 2, 3, 4, 5, 6}>
This will work for an any Array, as long as all elements are Fixnum.
If all you want is an array of the possible sums, flatten the array before getting the unique values.
puts b.flatten.uniq
What is happening is uniq is running over a multi-dimensional array. This causes it to look for duplicate arrays in your array. You'll need the array to be flattened first.
I have a pair of arrays,
array_1 = [1,2,3,4,5]
array_2 = [10,9,8,7,6]
and I'm trying to subtract the nth element of one array from the (n-1)-th element of the second array, starting with the n-th element, yielding an array of:
[9-1, 8-2, 7-3, 6-4] = [8, 6, 4, 2]
I wrote it in a procedural fashion:
array_1.pop
array_2.shift
[array_2,array_1].transpose.map { |a,b| a-b }
but I do not wish to alter the arrays. Is there a method or another way to go about this?
Another way:
enum1 = array_1.to_enum
enum2 = array_2.to_enum
enum2.next
arr = []
loop do
arr << enum2.next - enum1.next
end
arr
#=> [8, 6, 4, 2]
Use the non-destructive drop for the receiver, and zip, which will stop when the receiver runs out of an element even if the argument has more.
array_2.drop(1).zip(array_1).map{|a, b| a - b}
I think you may be overthinking it a bit; as long as both arrays are the same length, you can just iterate over the indices you care about, and reference the other array by index - offset.
array_1 = [1,2,3,4,5]
array_2 = [10,9,8,7,6]
n = 1
(n...array_1.length).map {|i| array_2[i] - array_1[i - 1] }
You can set n to whatever number you like and compute from that point onwards, so even if the arrays were tremendously large, you don't have to generate any intermediate arrays, and you don't have to perform any unnecessary work.
I have been working through Chris Pine's tutorial for Ruby and am currently working on a way to sort an array of names without using sort.
My code is below. It works perfectly but is a step further than I thought I had got!
puts "Please enter some names:"
name = gets.chomp
names = []
while name != ''
names.push name
name = gets.chomp
end
names.each_index do |first|
names.each_index do |second|
if names[first] < names[second]
names[first], names[second] = names[second], names[first]
end
end
end
puts "The names you have entered in alphabetical order are: " + names.join(', ')
It is the sorting that I am having trouble getting my head around.
My understanding of it is that each_index would look at the position of each item in the array. Then the if statement takes each item and if the number is larger than the next it swaps it in the array, continuing to do this until the biggest number is at the start. I would have thought that this would just have reversed my array, however it does sort it alphabetically.
Would someone be able to talk me through how this algorithm is working alphabetically and at what point it is looking at what the starting letters are?
Thanks in advance for your help. I'm sure it is something very straightforward but after much searching I can't quite figure it out!
I think the quick sort algorithm is one of the easier ones to understand:
def qsort arr
return [] if arr.length == 0
pivot = arr.shift
less, more = arr.partition {|e| e < pivot }
qsort(less) + [pivot] + qsort(more)
end
puts qsort(["George","Adam","Michael","Susan","Abigail"])
The idea is that you pick an element (often called the pivot), and then partition the array into elements less than the pivot and those that are greater or equal to the pivot. Then recursively sort each group and combine with the pivot.
I can see why you're puzzled -- I was too. Look at what the algorithm does at each swap. I'm using numbers instead of names to make the order clearer, but it works the same way for strings:
names = [1, 2, 3, 4]
names.each_index do |first|
names.each_index do |second|
if names[first] < names[second]
names[first], names[second] = names[second], names[first]
puts "[#{names.join(', ')}]"
end
end
end
=>
[2, 1, 3, 4]
[3, 1, 2, 4]
[4, 1, 2, 3]
[1, 4, 2, 3]
[1, 2, 4, 3]
[1, 2, 3, 4]
In this case, it started with a sorted list, then made a bunch of swaps, then put things back in order. If you only look at the first couple of swaps, you might be fooled into thinking that it was going to do a descending sort. And the comparison (swap if names[first] < names[second]) certainly seems to imply a descending sort.
The trick is that the relationship between first and second is not ordered; sometimes first is to the left, sometimes it's to the right. Which makes the whole algorithm hard to reason about.
This algorithm is, I guess, a strange implementation of a Bubble Sort, which I normally see implemented like this:
names.each_index do |first|
(first + 1...names.length).each do |second|
if names[first] > names[second]
names[first], names[second] = names[second], names[first]
puts "[#{names.join(', ')}]"
end
end
end
If you run this code on the same array of sorted numbers, it does nothing: the array is already sorted so it swaps nothing. In this version, it takes care to keep second always to the right of first and does a swap only if the value at first is greater than the value at second. So in the first pass (where first is 0), the smallest number winds up in position 0, in the next pass the next smallest number winds up in the next position, etc.
And if you run it on array that reverse sorted, you can see what it's doing:
[3, 4, 2, 1]
[2, 4, 3, 1]
[1, 4, 3, 2]
[1, 3, 4, 2]
[1, 2, 4, 3]
[1, 2, 3, 4]
Finally, here's a way to visualize what's happening in the two algorithms. First the modified version:
0 1 2 3
0 X X X
1 X X
2 X
3
The numbers along the vertical axis represent values for first. The numbers along the horizontal represent values for second. The X indicates a spot at which the algorithm compares and potentially swaps. Note that it's just the portion above the diagonal.
Here's the same visualization for the algorithm that you provided in your question:
0 1 2 3
0 X X X X
1 X X X X
2 X X X X
3 X X X X
This algorithm compares all the possible positions (pointlessly including the values along the diagonal, where first and second are equal). The important bit to notice, though, is that the swaps that happen below and to the left of the diagonal represent cases where second is to the left of first -- the backwards case. And also note that these cases happen after the forward cases.
So essentially, what this algorithm does is reverse sort the array (as you had suspected) and then afterwards forward sort it. Probably not really what was intended, but the code sure is simple.
Your understanding is just a bit off.
You said:
Then the if statement takes each item and if the number is larger than the next it swaps it in the array
But this is not what the if statement is doing.
First, the two blocks enclosing it are simply setting up iterators first and second, which each count from the first to the last element of the array each time through the block. (This is inefficient but we'll leave discussion of efficient sorting for later. Or just see Brian Adkins' answer.)
When you reach the if statement, it is not comparing the indices themselves, but the names which are at those indices.
You can see what's going on by inserting this line just before the if. Though this will make your program quite verbose:
puts "Comparing names[#{first}] which is #{names[first]} to names[#{second}] which is #{names[second]}..."
Alternatively, you can create a new array and use a while loop to append the names in alphabetical order. Delete the elements that have been appended in the loop until there are no elements left in the old array.
sorted_names = []
while names.length!=0
sorted_names << names.min
names.delete(names.min)
end
puts sorted_names
This is the recursive solution for this case
def my_sort(list, new_array = nil)
return new_array if list.size <= 0
if new_array == nil
new_array = []
end
min = list.min
new_array << min
list.delete(min)
my_sort(list, new_array)
end
puts my_sort(["George","Adam","Michael","Susan","Abigail"])
Here is my code to sort items in an array without using the sort or min method, taking into account various forms of each item (e.g. strings, integers, nil):
def sort(objects)
index = 0
sorted_objects = []
while index < objects.length
sorted_item = objects.reduce do |min, item|
min.to_s > item.to_s ? item : min
end
sorted_objects << sorted_item
objects.delete_at(objects.find_index(sorted_item))
end
index += 1
sorted_objects
end
words_2 = %w{all i can say is that my life is pretty plain}
p sort(words_2)
=> ["all", "can", "i", "is", "is", "life", "my", "plain", "pretty", "say", "that"]
mixed_array_1 = ["2", 1, "5", 4, "3"]
p sort(mixed_array_1)
=> [1, "2", "3", 4, "5"]
mixed_array_2 = ["George","Adam","Michael","Susan","Abigail", "", nil, 4, "5", 100]
p sort(mixed_array_2)
=> ["", nil, 100, 4, "5", "Abigail", "Adam", "George", "Michael", "Susan"]
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Ruby - What is the difference between map, each and collect?
I have looked in Ruby-Doc also but i cant understand the difference between
map
each
iterators.It would be great if you could give an example and explain.
each simply iterates over the given enumerable, running the block for each value. It discards the return value of the block, and each simply returns the original object it was called on:
[1, 2, 3].each do |x|
x + 1
end # => [1, 2, 3]
This is simply a nicer, more universal way of doing a traditional iterating for loop, and each is much preferred over for loops in Ruby (in fact, I don't think I've ever used a for loop in Ruby).
map, however, iterates over each element, using the return value of the block to populate a new array at each respective index and return that new array:
[1, 2, 3].map do |x|
x + 1
end # => [2, 3, 4]
So it “maps” each element to a new one using the block given, hence the name “map”. Note that neither each nor map themselves modify the original collection. This is a concise, functional alternative to creating an array and pushing to it in an iterative loop.
each returns the original object. It's used to run an operation using each element of an array without collecting any of the results. For example, if you want to print a list of numbers, you might do something like this:
arr = [1, 2, 3, 4]
arr.each { |n| puts n }
Now, that puts method above actually returns nil. Some people don't know that, but it doesn't matter much anyway; there's no real reason to collect that value (if you wanted to convert arr to strings, you should be using arr.map(&:to_s) or arr.map { |n| n.to_s }.
map returns the results of the block you pass to it. It's a great way to run an operation on each element in an array and retrieve the results. If you wanted to multiple every element of an array by 2, this is the natural choice. As a bonus, you can modify the original object using map!. For example:
arr = [1, 2, 3, 4]
arr.map! { |n| n * 2}
# => [2, 4, 6, 8]