I have two arrays, I want to return the larger number from the same position in each array.
def get_larger_numbers(a, b)
c = []
count = 0
while count < 10 #assumes there are less than 10 elements in an array, not an ideal solution.
if a[count] > b[count]
c << a[count]
elsif b[count] > a[count]
c << b[count]
else #if numbers are the same
c << a[count]
end
count+= 1
end
return c
end
a = [13, 64, 15, 17, 88]
b = [23, 14, 53, 17, 80]
should return:
c == [23, 64, 53, 17, 88]
Clearly, my code doesn't work, what's the best way to refer to increasing index positions?
Also interested to know simpler ways of doing this.
Your code isn't working because of the static 10 you have as the length. Instead I suggest you make your code more dynamic with regards to how often you loop.
def get_larger_numbers(a,b)
c = []
[a.length, b.length].min.times do |i|
if a[i] > b[i]
c << a[i]
else
c << b[i]
end
end
c
end
a = [13, 64, 15, 17, 88]
b = [23, 14, 53, 17, 80]
get_larger_numbers(a,b)
#=> [23, 64, 53, 17, 88]
This solution assumes that if the arrays are not equal size, you want to throw the rest away.
Okay... Here's what you should do:
def get_larger_numbers(a, b)
c = [] #declare empty array for answer
for i in 0...(a.length < b.length ? a.length : b.length) #see EDIT note
c << (a[i] > b[i] ? a[i] : b[i])
end
c #this is an implicit return
end
a = [13, 64, 15, 17, 88]
b = [23, 14, 53, 17, 80]
puts get_larger_numbers(a,b)
This'll do a for loop that'll run from 0 to the length of a. Yes, it assumes that they're the same length. I figure this is what you want.
Anyway, there's a simple ternary that compares the value of each element in both arrays, one index at a time.
It'll push the bigger value to the c array, leaving you with the greater values in the c array to be returned.
EDIT: Added the ternary expression so that for loops through only the smaller array, because comparing with nil (which is what is at any n index beyond the array, presumably) would raise an error.
A compact solution would be:
def get_larger_numbers(a, b)
return a.zip(b).map{|x, y| (x >= y) ? x : y } # Return optional, added for clarity
end
a = [13, 64, 15, 17, 88]
b = [23, 14, 53, 17, 80]
p get_larger_numbers(a, b)
Note that this assumes the input arrays are of the same length. If arrays are of unequal length, you can truncate to the length of the shorter array, or pad the end with the unpaired elements of the larger array. The current code will throw an error, letting you know you've hit this unspecified case.
As for how it works, the zip pairs the elements of the two arrays, so a.zip(b) becomes:
[[13, 23], [64, 14], [15, 53], [17, 17], [88, 80]]
It then loops over the array with map to produce a new array, passing each pair into the block, which returns the larger of the two elements to fill the output array.
Assuming the two arrays are the same size, simply:
def largest_by_position(a,b)
a.zip(b).map(&:max)
end
largest_by_position([13, 64, 15, 17, 88], [23, 14, 53, 17, 80])
#=> [23, 64, 53, 17, 88]
Alternatively, make the operative line:
[a,b].transpose.map(&:max)
For equal-size arrays a and b, Enumerable#zip and Array#transpose always have this yin and yang relationship:
a.zip(b) == [a,b].transpose #=> true
Related
I feel like I'm close but not quite sure why my while loop stops executing, I want it to run/ increase the counter then the conditions are true, then when it runs into numbers out of order, swap them, then decrease the counter, then run the while loop again until all the numbers are in order. So like it slides the number that's out of order backwards until it's higher than the number before it but lower than then number after it. If that makes sense.Probably an easy one for most of you but I'm just new to python. Here is my code so far;
arr = [7, 14, 21, 32, 17, 48, 69, 78, 72]
count = 0
while (count < len(arr) - 1) and (arr[count] < arr[count+1]):
count += 1
if (arr[count] > arr[count+1]):
arr[count], arr[count+1] = arr[count+1], arr[count]
count -= 1
continue
print(count)
print(arr)
below my code with some pseudocode to make it clearer.
# list of numbers out of order
arr = [7, 14, 21, 32, 17, 48, 69, 78, 72]
# first position in index
count = 0
# loop to check first if value of index position is less than length of array -1 (9-1 = 8)
# or if value of index position is less than index position + 1 (next index position)
while (count < len(arr) - 1) and (arr[count] < arr[count+1]):
# if true loop should continue + 1 on the next index
count += 1
# if the value of number in the current index position is greater than the next number it swaps them.
if (arr[count] > arr[count+1]):
arr[count], arr[count+1] = arr[count+1], arr[count]
count -= 1
continue
print(count)
print(arr)
I've tried various different things, I think I'm just stuck on how while loops actually work and I need to get the loop to run again after it hits it's first false statement.
This would work in the way that is required in the question:
finds the first two numbers that are out of order.
switched those numbers only and exit.
arr = [7, 14, 21, 32, 17, 48, 69, 78, 72]
for i, v in enumerate(arr):
if i == len(arr) -1:
print('reached the end')
break
if v > arr[i+1]:
print('out of order index', i, 'value:',v)
# do the switch
arr[i], arr[i+1] = arr[i+1], v
break
print(arr)
the result is this:
out of order index 3 value: 32
[7, 14, 21, 17, 32, 48, 69, 78, 72]
You could also achieve the same with a while loop so long as a break condition existed.
So, I have this array:
numbers = [5, 9, 3, 19, 70, 8, 100, 2, 35, 27]
What I want to do is to create another array from this one, but now each value of this new array must be equal to the corresponding value
in the numbers array multiplied by the following.
For example: the first value of the new array should be 45, as it is the multiplication
of 5 (first value) and 9 (next value). The second value of the new array should be 27, as it is the multiplication of 9 (second
value) and 3 (next value), and so on. If there is no next value, the multiplication must be done by 2.
So, this array numbers should result in this other array: [45, 27, 57 ,1330, 560, 800, 200, 70, 945, 54]
I only managed to get to this code, but I'm having problems with index:
numbers = [5,9,3,19,70,8,100,2,35,27]
new_array = []
x = 0
while x <= 8: # Only got it to work until 8 and not the entire index of the array
new_array.append(numbers[x] * numbers[x + 1])
x += 1
print(new_array)
How can I make it work no matter what is index of the array and then if there's no next number, multiply it by 2? I've tried everything but this was the closest I could get.
Try:
numbers = [5, 9, 3, 19, 70, 8, 100, 2, 35, 27]
out = [a * b for a, b in zip(numbers, numbers[1:] + [2])]
print(out)
Prints:
[45, 27, 57, 1330, 560, 800, 200, 70, 945, 54]
Andrej Kesely's approach is totally fine and would be the way to go for an experienced python developer.
But I assume you are kind of new to python, so here is a more simple approach if you are a bit familiar with other programming languages:
#function called multiply, taking an int[], returns int[]
def multiply(values):
newData = []
valuesLength = len(values) - 1
for i in range(valuesLength):
newData.append(values[i] * values[i+1])
newData.append(values[valuesLength] * 2)
return newData
#init int[], calling multiply-function and printing the data
numbers = [5,9,3,19,70,8,100,2,35,27]
newData = multiply(numbers)
print(newData)
The multiply-Function basically initiates an empty array, then loops over the passed values, multiplying them with the following value, leaves the loop one value too early and finally adds the last value by multiplying it with 2.
With the same approach as you did, making use of len(numbers):
numbers = [5,9,3,19,70,8,100,2,35,27]
new_array = []
x = 0
while x < len(numbers):
nxt = 2 if x+1 >= len(numbers) else numbers[x+1]
new_array.append(numbers[x] * nxt)
x += 1
print(new_array)
NOTE: The shorthand for nxt = 2.... is explained in the first comment of this answer: https://stackoverflow.com/a/14461963/724039
I have two arrays as follows:
a = [1,2,3,4,5,6,7,8,9,10]
b = [3,5,8,10,11]
I want to find the index of subarray in main array if a number is present. The expected output is:
res = [2,4,7,9]
I have done as follows:
[3,5,8,10,11].each do |_element|
res_array = []
if [1,2,3,4,5,6,7,8,9,10].find_index(_element).present?
res_array << (header_array.find_index(_element)
end
res_array
end
But I think there is a better approach to do this.
If performance matters (i.e. if your arrays are huge), you can build a hash of all number-index pairs in a, using each_with_index and to_h:
a.each_with_index.to_h
#=> {1=>0, 2=>1, 3=>2, 4=>3, 5=>4, 6=>5, 7=>6, 8=>7, 9=>8, 10=>9}
A hash allows fetching the values (i.e. indices) for the numbers in b much faster (as opposed to traversing an array each time), e.g. via values_at:
a.each_with_index.to_h.values_at(*b)
#=> [2, 4, 7, 9, nil]
Use compact to eliminate nil values:
a.each_with_index.to_h.values_at(*b).compact
#=> [2, 4, 7, 9]
or alternatively slice and values:
a.each_with_index.to_h.slice(*b).values
#=> [2, 4, 7, 9]
b.map { |e| a.index(e) }.compact
#⇒ [2, 4, 7, 9]
or, more concise:
b.map(&a.method(:index)).compact
Here is another simpler solution,
indxs = a.each_with_index.to_h
(a&b).map{|e| indxs[e]}
All the answers so far traverse all of a once (#Stefan's) or traverse all or part of a b.size times. My answer traverses part or all of a once. It is relatively efficient when a is large, b is small relative to a and all elements in b appear in a.
My solution is particularly efficient when a is ordered in such a way that the elements of b typically appear towards the beginning of a. For example, a might be a list of last names sorted by decreasing frequency of occurrence in the general population (e.g., ['smith', 'jones',...]) and b is a list of names to look up in a.
a and b may contain duplicates1 and not all elements of b are guaranteed to be in a. I assume b is not empty.
Code
require 'set'
def lookup_index(a, b)
b_set = b.to_set
b_hash = {}
a.each_with_index do |n,i|
next unless b_set.include?(n)
b_hash[n] = i
b_set.delete(n)
break if b_set.empty?
end
b_hash.values_at(*b)
end
I converted b to a set to make lookups comparable in speed to hash lookups (which should not be surprising considering that sets are implemented with an underlying hash). Hash lookups are very fast, of course.
Examples
a = [1,2,3,4,5,6,7,8,9,10,8]
b = [3,5,8,10,11,5]
Note that in this example both a and b contain duplicates and 11 in b is not present in a.
lookup_index(a, b)
#=> [2, 4, 7, 9, nil, 4]
Observe the array returned contains the index 4 twice, once for each 5 in b. Also, the array contains nil at index 4 to show that it is b[4] #=> 11 that does not appear in a. Without the nil placeholder there would be no means to map the elements of b to indices in a. If, however, the nil placeholder is not desired, one may replace b_hash.values_at(*b) with b_hash.values_at(*b).compact, or, if duplicates are unwanted, with b_hash.values_at(*b).compact.uniq.
As a second example suppose we are given the following.
a = [*1..10_000]
b = 10.times.map { rand(100) }.shuffle
#=> [30, 62, 36, 24, 41, 27, 83, 61, 15, 55]
lookup_index(a, b)
#=> [29, 61, 35, 23, 40, 26, 82, 60, 14, 54]
Here the solution was found after the first 83 elements of a were enumerated.
1 My solution would be no more efficient if duplicates were not permitted in a and/or b.
I have two different arrays. Let's say:
a = [1, 2, 13, 4, 10, 11, 43]
b = [44, 23, 1, 4, 10, 2, 55, 13]
Now I have to sort the array b by referring to the array a. I tried the following solution:
lookup = {}
a.each_with_index do |item, index|
lookup[item] = index
end
b.sort_by do |item|
lookup.fetch(item)
end
But I'm getting the KeyError: key not found: 44 error. Can anyone help me find a solution?
Expected output is [1, 2, 13, 4, 10, 23, 44, 55].
Comparing arrays checks the first value, if it's equal goes to the second value and so on. Hence this will compare by the order of occurrence in a and then by the actual value for the ones not in a:
b.sort_by { |e| [a.index(e) || a.size, e] }
To keep O(nlogn), you could:
ai = a.each_with_index.to_h
b.sort_by { |e| [ai[e] || a.size, e] }
Lets say I have an #assortment of numbers in a hash, e.g. 1 to 100.
Each number in the #assortment can have a status of :free, or :used.
An example #assortment could be:
{ 1 => :free, 2 => :free, 3=> :used etc ... }
Lets say I want to split the #assortment up based on the used numbers, and extract the free numbers into their own hash (or an array or hashes?)
For example, for an #assortment of 1 to 100, if numbers 25 and 75 were 'used' and the rest were 'free', then the result would be 3 new hashes of all the free values:
1 to 24
26 to 74
76 to 100
Similarly, lets say we have a different #assortment, with numbers 1 to 100, but I want to extract numbers 20 to 80, but numbers 30, 31, 32 and 40 are used then the result is like this :
hash1 -> 20 to 29
hash2 ->33 to 39
hash3 -> 41 to 80
Is there a nice functional way to do this in Ruby, where I can pass in a complete #assortment of numbers, and an optional range to extract and get the resulting hashes, perhaps in an array?
I guess the original hash gets broken up or split based on the :used elements...
If you were to loop through the hash, then every free number would be added to a new hash (e.g. hash1) until you reach a used number. Keep going through the loop until you reach a free number, this and all subsequent free numbers get added to a new hash (hash2). Keep this going until you have all the free numbers in new hashes...
#assortment = (20..50).to_a.product([:free]).to_h
[30,31,32,40].each { |n| #assortment[n] = :used }
#assortment
# => {20=>:free, 21=>:free, 22=>:free, 23=>:free, 24=>:free, 25=>:free,
# 26=>:free, 27=>:free, 28=>:free, 29=>:free, 30=>:used, 31=>:used,
# 32=>:used, 33=>:free, 34=>:free, 35=>:free, 36=>:free, 37=>:free,
# 38=>:free, 39=>:free, 40=>:used, 41=>:free, 42=>:free, 43=>:free,
# 44=>:free, 45=>:free, 46=>:free, 47=>:free, 48=>:free, 49=>:free, 50=>:free}
Return an array of hashes
#assortment.reject { |_,v| v == :used }.
slice_when { |(a,_),(b,_)| b > a+1 }.
to_a.
map(&:to_h)
#=> [{20=>:free, 21=>:free,...29=>:free},
# {33=>:free, 34=>:free,...39=>:free},
# {41=>:free, 42=>:free,...50=>:free}]
See Hash#reject (which returns a hash) and Enumerable#slice_when.
Return an array of arrays
Having a hash whose values are all the same doesn't seem very useful. If you'd prefer returning an array of array, just drop to_h.
arr = #assortment.reject { |_,v| v == :used }.
keys.
slice_when { |a,b| b > a+1 }.
to_a
#=> [[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
# [33, 34, 35, 36, 37, 38, 39],
# [41, 42, 43, 44, 45, 46, 47, 48, 49, 50]]
Return an array of ranges
A third option is to return an array of ranges. To do that map each of arr's elements (arrays) to a range:
arr.map { |f,*_,l| f..l }
#=> [20..29, 33..39, 41..50]
The first element of arr passed to the block is [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]. The three block variables are computed using parallel assignement:
f,*b,l = [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]
f #=> 20
_ #=> [21, 22, 23, 24, 25, 26, 27, 28]
l #=> 29
I wish to underscore that I've used an underscore for the second block variable to underscore that it is not used in the block calculation.