Delete every other item in the array until only one is left - arrays

My problem is asking to iterate over an array of items and delete every other item until I reach the end of the array when I should start iterating backwards and keep deleting every other item and so on back and forth until only one item is left in the array.
for example: 1,2,3,4,5, would become 1,3,5 and then 3
I understand that I shouldn't be deleting from the original array so I created another array that just keeps every other item while decreasing its size but I can't get it to work and end up with an infinite loop.
arr=*(1..10)
s_arr=[]
until s_arr.length==1
i=0
while i<arr.length
s_arr.push(arr[i])
i+=2
end
arr=s_arr.reverse
s_arr=arr
end
Thank you.

Each iteration, replace the array with a version with every other element dropped
arr = *(1..10)
while arr.length > 1
arr = arr.select.with_index { |_, i| i % 2 == 0 }.reverse
end
This doesn't match your example, but your example seems inconsistent in the first place. If [1,2,3,4,5] goes to [1,3,5], then the next step should be [1,5] instead of [3].

This is similar to #Max's answer, but implemented recursively.
def last_one_standing(arr)
return arr.first if arr.size == 1
last_one_standing arr.select.with_index { |_,i| i.even? }.reverse
end
(1..16).each { |i| puts "1..%d: %d" % [i, last_one_standing((1..i).to_a)] }
1..1: 1
1..2: 1
1..3: 3
1..4: 3
1..5: 1
1..6: 1
1..7: 3
1..8: 3
1..9: 9
1..10: 9
1..11: 11
1..12: 11
1..13: 9
1..14: 9
1..15: 11
1..16: 11

try something like this so you can take advantage of Ruby's power
result = []
array.each_with_index {|val,idx| result << val if ( idx % 2 == 0)}

Try this on your ruby console:
arr=*(1..10)
x = (Math::log(arr.length) / Math::log(3)).to_i
3**x

arr=*(1..10)
s_arr=[]
until s_arr.length == 1
i=0
while i < arr.length
s_arr.push(arr[i])
i+=2
end
arr=s_arr.reverse
if s_arr.length != 1
s_arr=[]
end
end
Your problem was setting the s_arr array the same as the arr. Then you must add a conditional to tell it to stop when it does reach 1 otherwise it'll become 0 instead.
EDIT:
arr=*(1..10)
s_arr=[]
until s_arr.length == 1
s_arr = []
i=0
while i < arr.length
s_arr.push(arr[i])
i+=2
end
arr=s_arr.reverse
end
even better solution.

Related

Trouble understanding one line in Insertion Sort (Python 3)

I was looking through the other threads concerning the Insertion Sort but didn't find the answer that relates to the specific part of it which I do not understand.
My full Insertion Sort algorithm can be seen below. It works as intended, but I cannot figure out the purpose of that last line.
array[position] = value
Let me explain through an example:
When the 'for' loop starts, index = 1.
=> value = array[1] = 3
=> position = 1
=> Since 4 > 3, the item at index 1, is swapped with the item at index 0.
=> position -= 1
=> position = 0
We now get to the line which confuses me:
array[0] = value
=> value = array[index] = array[0] = 3
However, when the 'for' loop goes through its second iteration, index = 2.
And so immediately value = array[2] and position = 2 ?
Even though I know that this last line of code is necessary I just cannot see what purpose it serves.
Could someone please explain to me this final logical step?
Thank you in advance for your time and help.
array = [4,3,5,6,12,9,8,6]
for index in range (1, len(array)):
value = array[index]
position = index
while position > 0 and array[position - 1] > value:
array[position] = array[position - 1]
position -= 1
array[position] = value
The position variable represents (at the end) where you will eventually insert the number. Without the last line, you are not inserting the number at 'position' at the correct place (it will result in the 1 index after). Below is my code for insertion sort.
def insertionSort(arr):
length = len(arr)
for i in range(1, length):
currNum = arr[i]
j = i - 1
while j >= 0 and currNum < arr[j]:
arr[j + 1] = arr[j]
j -= 1
arr[j+1] = currNum
return arr
You wouldn't have really swapped the numbers without that line, would you?
index 1:
-> [4,3,5,6,12,9,8,6] (enters while loop)
-> [4,4,5,6,12,9,8,6] ("pushes" 4 to position 1)
-> <waits for while loop to finish>
-> [3,4,5,6,12,9,8,6] (assigns 3 to position 0 since there are no other elements > 3)
index 2:
...
I think is not really needed at start in for loop, because it was used to index inside the loop. Looks the picture

Ruby: NoMethodError when comparing element in an array

I'm working on a method that takes an array of words as a param and returns an array of arrays, where each subarray contains words that are anagrams of each other. The line while sort_array[i][1]==temp do is throwing undefined method '[]' for nil:NilClass (NoMethodError) and I have no idea why.
def combine_anagrams(words)
sort_array = Hash.new
words.each do |w|
sort_array[w] = w.split("").sort
end
sort_array = sort_array.sort_by { |w, s| s }
return_array = []
i = 0
while i<sort_array.length do
temp_array = []
temp = sort_array[i][1]
while sort_array[i][1]==temp do
temp_array += [sort_array[i][0]]
i+=1
end
return_array += [temp_array]
end
return temp_array
end
p combine_anagrams( ['cars', 'for', 'potatoes', 'racs', 'four','scar', 'creams','scream'] )
It looks like this is because you are incrementing your i variable without checking to make sure you're still in bounds of the sort_array. To see the problem, put an puts statement in your code inside the inner most while loop:
while sort_array[i][1]==temp do
temp_array += [sort_array[i][0]]
i+=1
puts "i is #{i} and the length is #{sort_array.length}"
end
and then run your code and you'll see:
i is 1 and the length is 8
i is 2 and the length is 8
i is 3 and the length is 8
i is 4 and the length is 8
i is 5 and the length is 8
i is 6 and the length is 8
i is 7 and the length is 8
i is 8 and the length is 8
example.rb:15:in `combine_anagrams': undefined method `[]' for nil:NilClass (NoMethodError)
You need to make sure on both while loops that you stay within the bounds of your array, for instance:
while i < sort_array.length && sort_array[i][1]==temp do
end
As a side note, your code is currently only going to return the last temp_array, since you're also resetting that at the beginning of your outer while loop. And, if I understand what problem you're trying to solve you might want to look at group_by available on Array thanks to the Enumerable module:
words = ['cars', 'for', 'potatoes', 'racs', 'four','scar', 'creams','scream']
words.group_by { |word| word.chars.sort }.values
# => [["cars", "racs", "scar"], ["for"], ["potatoes"], ["four"], ["creams", "scream"]]

Can I manipulate the current iteration of the collection being currently iterated over?

Say I have a collection of #lines, and I want to iterate over it, but I want to manipulate how it is iterated over based on the contents of the collection, how do I do that?
i.e. something like this:
#lines.each_with_index do |line, index|
if (index % 3 = 0)
jump_to index == next_multiple_of_3
end
end
So what that may look like is something like this:
Element | Index
a | 1
b | 2
c | 3
f | 6
g | 7
h | 8
i | 9
l | 12
How can I do that?
Edit 1
Note that, as I explained in a comment below, I don't necessarily always want to jump to a specific multiple. Basically, I would want to jump to some arbitrary index that is determined by something that happens within the current iteration.
So the first go around, it could jump 3 indices up, but then the next time it jumps 7 indices up, then for the next 5 times it jumps by 1 like normal, then it jumps by 2 indices up.
The only constant is that the way to determine how it progresses through the original iteration is based on something that happens within the block of the current iteration.
Use a while loop and manage the index yourself. We were discouraging people from doing this for ages. But this is the one case where it's appropriate. :)
idx = 0
while idx < #lines.length
line = #lines[idx]
idx += condition ? x : y # or whatever logic you have
end
This, of course, assumes that #lines is capable of random access. If it's not, make it an array.
If you want to use this logic multiple times, you might want to define an Enumerable method :
module Enumerable
def skip
Enumerator.new do |yielder|
skip_count = 0
each_with_index do |object, index|
if skip_count > 0
skip_count -= 1
else
yielder << object
skip_count = yield(object, index) || 0
end
end
end
end
end
You can use it on any Enumerable, and you can specify the number of elements that should be skipped, depending on the current element and index.
For your example, you want to skip 2 elements (e.g. 4 and 5), every 6 elements. There's an offet though, you want to skip the elements after index = 2 (Ruby indices are 0-based) :
puts ('a'..'z').skip{ |_, index| 2 if (index - 2) % 6 == 0 }.take(8)
# a
# b
# c
# f
# g
# h
# i
# l
One might do it using the temporary accumulator:
#lines = (1..12).map(&:to_s)
JUMP_BY = 3
#lines.each.with_index.reduce(0) do |acc, (line, index)|
next acc unless index == acc
puts "line: #{line}"
q, mod = index.divmod(JUMP_BY)
(mod == JUMP_BY - 1) && (q % 2).zero? ? \
(q + 2) * JUMP_BY - 1 : index + 1 # next
end
#⇒ line: 1
# line: 2
# line: 3
# line: 6
# line: 7
# line: 8
# line: 9
# line: 12
By making JUMP_BY a method, one might achieve as complicated decisions as needed.
You are making it more complicated than necessary. You don't need to jump in iteration. Just ignore the task when the condition is (un)satisfied, and go to the next iteration.
#lines.each_with_index do |line, index|
next if index % 3 == 0
... # do the contentful things
end
By using next on a Enumerator you can jump x steps
def walk
e = ('a'..'z').each
while e.any?
rand(5).times{e.next}
puts e.peek
end
rescue StopIteration => e
end

Return a Boolean if an element occurs three times in a row in an array

I am trying to write a method that takes an array and returns trueif there is an element that occurs three times in a row or false if it doesn't. I can't think of the syntax. Would you use count? See the example below.
def got_three?(array)
end
got_three?([1,2,2,3,4,4,4,5,6]) would return true as 4 shows up three times in a row.
Toying with the new Ruby 2.3.0 method chunk_while:
def got_three?(array)
array.chunk_while(&:==).any?{|g| g.size >= 3}
end
With Enumerable#chunk:
def got_three?(xs)
xs.chunk(&:itself).any? { |y, ys| ys.size >= 3 }
end
Not so smart but a naive one (using a instead of array since it is long):
a.each_index.any?{|i| a[i] == a[i + 1] and a[i + 1] == a[i + 2]}
I assume you don't have any nil in the array.
An alternative which may be more performant (as per #sawa's comments)...
def got_three?(array)
(0..(array.count-2)).any?{|i|array[i] == array[1+1] && array[i] == array[i+2]}
end
Look, ma, no indices!
def ducks_in_a_row?(arr, n)
cnt = 0
last = arr.first
arr.each do |d|
if d==last
cnt += 1
return true if cnt==n
else
last = d
cnt = 1
end
end
false
end
ducks_in_a_row?([1,2,3,4,5,6,6,7,7,7], 3)
#=> true
def got_three?(array)
array.each_cons(3).map{|g|g.uniq.length == 1}.any?
end
or as #wandmaker suggests...
def got_three?(array)
array.each_cons(3).any?{|g|g.uniq.length == 1}
end
Here is my take on this problem - I tried to make it generic, hopefully efficient so that the loop terminates as soon as n-consecutive elements are found.
def got_consecutive?(array, n = 3)
case array.size
when 0...n
return false
when n
return array.uniq.size == n
else
array[n..-1].each_with_object(array[0...n]) do |i, t|
(t.uniq.size == 1 ? (break t) : (t << i).shift)
end.uniq.size == 1
end
end
p got_consecutive?([])
#=> false
p got_consecutive?([1,2])
#=> false
p got_consecutive?([1,2,2,3,2,3,3,3], 3)
#=> true
p got_consecutive?([1,2,2,3,2,3,3,3], 4)
#=> false
p got_consecutive?([1,2,2,3,2,3,3,3,3,3,4,4,4,4], 5)
#=> true
The code takes care of border cases first such as when array did not have n elements in which case answer is obviously false, and another one being when the array had only n elements - in which case just a uniqueness check would suffice.
For cases where array size is greater than n, the code uses Enumerable#each_with_object with the initial object being an array of n elements from the array - this array is used also as temporary work area to track n consecutive elements and perform a check whether all those elements are same or not.

Find repeated element in array

Consider array of INT of positive numbers:
{1,3,6,4,7,6,9,2,6,6,6,6,8}
Given: only one number is repeated, return number and positions with efficient algorithm.
Any ideas for efficient algorithms?
One possible solution is to maintain an external hash map. Iterate the array, and place the indices of values found into the hash map. When done, you now know which number was duplicated and the indices of the locations it was found at.
In an interview situation, I guess its your chance to ask around the question, for example, how many numbers? what range of numbers? you could state that an optimum algorithm could change depending.
That gives you a chance to show how you solve problems.
If the range of ints in the array is small enough then you could create another array to keep count of the number of times each integer is found then go linearly through the array accumulating occurrence counts, stopping when you get to an occurance count of two.
Hash will do just fine in here. Add numbers to it one by one, each time checking if number's already in there.
Well, there probably is some trick (usually is). But just off the cuff, you should be able to sort the list (O(nlogn)). Then its just a matter of finding a number that is the same as the next one (linear search - O(n)). You'd have to sort it as tuples of values and original indices of course, so you could return that index you are looking for. But the point is that the upper bound on an algorithim that will do the job should be O(nlogn).
If you just go through the list linerally, you could take each index, then search through the rest of the list after it for a matching index. I think that's roughly equivalent to the work done in a bubble sort, so it would probably be O(n^2), but a simple one.
I really hate trick questions as interview questions. They are kind of like optical illusions: Either you see it or you don't, but it doesn't really say anything bad about you if you don't see the trick.
I'd try this:
all elms of list have to be looked at (=> loop over the list)
before the repeated elm is known, store elm => location/index in a hash/dictionary
as soon as the second occurence of the repeated element is found, store its first postion (from the hash) and the current position in the result array
compare further elms of list against the repeated elm, append found locations to the result array
in code:
Function locRep( aSrc )
' to find repeated elm quickly
Dim dicElms : Set dicElms = CreateObject( "Scripting.Dictionary" )
' to store the locations
Dim aLocs : aLocs = Array()
' once found, simple comparison is enough
Dim vRepElm : vRepElm = Empty
Dim nIdx
For nIdx = 0 To UBound( aSrc )
If vRepElm = aSrc( nIdx ) Then ' repeated elm known, just store location
ReDim Preserve aLocs( UBound( aLocs ) + 1 )
aLocs( UBound( aLocs ) ) = nIdx
Else ' repeated elm not known
If dicElms.Exists( aSrc( nIdx ) ) Then ' found it
vRepElm = aSrc( nIdx )
ReDim aLocs( UBound( aLocs ) + 2 )
' location of first occurrence
aLocs( UBound( aLocs ) - 1 ) = dicElms( aSrc( nIdx ) )
' location of this occurrence
aLocs( UBound( aLocs ) ) = nIdx
Else
' location of first occurrence
dicElms( aSrc( nIdx ) ) = nIdx
End If
End If
Next
locRep = aLocs
End Function
Test run:
-------------------------------------------------
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0
Src: 1 3 6 4 7 6 9 2 6 6 6 6 8
Res: 2 5 8 9 10 11
ok
Src:
Res:
ok
Src: 1 2 3
Res:
ok
Src: 1 1 2 3 4 5 6
Res: 0 1
ok
Src: 1 2 3 4 5 6 6
Res: 5 6
ok
=================================================
using namespace std;
list<int> find_duplicate_idx(const vector<int>& A)
{
hash_map<int, int> X;
list<int> idx;
for ( int i = 0; i < A.size(); ++ i ) {
hash_map<int, int>::iterator it = X.find(A[i]);
if ( it != X.end() ) {
idx.push_back(it->second);
idx.push_back(i);
for ( int j = i + 1; j < A.size(); ++j )
if ( A[j] == A[i] )
idx.push_back(j);
return idx;
}
X[A[i]] = i;
}
return idx;
}
This is a solution my friend provided. Thank you SETI from mitbbs.com
Use the hash-map to solve it :
private int getRepeatedElementIndex(int[] arr) {
Map<Integer, Integer> map = new HashMap();
// find the duplicate element in an array
for (int i = 0; i < arr.length; i++) {
if(map.containsKey(arr[i])) {
return i;
} else {
map.put(arr[i], i);
}
}
throw new RuntimeException("No repeated element found");
}
Time complexity : O(n)
Space complexity : O(n)

Resources