delete ONE array element by value in ruby - ruby

How can I delete the first element by a value in an array?
arr = [ 1, 1, 2, 2, 3, 3, 4, 5 ]
#something like:
arr.delete_first(3)
#I would like a result like => [ 1, 1, 2, 2, 3, 4, 5]
Thanks in advance

Pass the result of Array#find_index into Array#delete_at:
>> arr.delete_at(arr.find_index(3))
>> arr
=> [1, 1, 2, 2, 3, 4, 5]
find_index() will return the Array index of the first element that matches its argument. delete_at() deletes the element from an Array at the specified index.
To prevent delete_at() raising a TypeError if the index isn't found, you may use a && construct to assign the result of find_index() to a variable and use that variable in delete_at() if it isn't nil. The right side of && won't execute at all if the left side is false or nil.
>> (i = arr.find_index(3)) && arr.delete_at(i)
=> 3
>> (i = arr.find_index(6)) && arr.delete_at(i)
=> nil
>> arr
=> [1, 1, 2, 2, 3, 4, 5]

You can also use :- operator to remove desired element from the array, e.g.:
$> [1, 2, 3, '4', 'foo'] - ['foo']
$> [1, 2, 3, '4']
Hope this helps.

Related

Conditionally inserting data into an array

Alright, can someone help me how to properly iterate a dynamic size array?
Here's what I mean:
my_array = [1, 2, 3, 4, 2, 5, 15, 2] # <= there are three 2 inside my_array
Then I would like to add "good" everytime the iteration hit integer 2, I tried several method but not find the way, here's the best method I've tried(still not resulting in what I want)
# First Method
for i in 0..(my_array.length - 1)
my_array.insert(i + 1, "good") if my_array[i] == 2
end
p my_array # => [1, 2, "good", 3, 4, 2, "good", 5, 15, 2]
# Second Method
for i in 0..(my_array.length - 1)
my_array[i + 1] = "good" if my_array[i] == 2
end
p my_array # => [1, 2, "good", 4, 2, "good", 15, 2, "good"]
The first method is not good because it's not showing "good" after the last 2, I guess this because the iteration could not reach the last integer 2(in the last array) and that is expected because the array size is changed bigger everytime "good" is inserted.
The second one is also bad, because I replace the data after every 2 with "good" string.
Now can someone point it out to me how can I doing this properly so I can produce it like this:
p my_array # => [1, 2, "good", 3, 4, 2, "good", 5, 15, 2, "good"]
All "good" is added without replacing any data.
Any help is appreciated, thank you very much.
You'd have a better time transforming this into a new array than modifying in-place:
my_array = [1, 2, 3, 4, 2, 5, 15, 2]
def add_good(a)
a.flat_map do |value|
case (value)
when 2
[ 2, 'good' ]
else
value
end
end
end
puts add_good(my_array).inspect
# => [1, 2, "good", 3, 4, 2, "good", 5, 15, 2, "good"]
The flat_map method is useful for situations where you want to create zero or more entries in the resulting array. map is a 1:1 mapping, flat_map is a 1:N.
It's also possible to make this much more generic:
def fancy_insert(a, insert)
a.flat_map do |value|
if (yield(value))
[ value, insert ]
else
value
end
end
end
result = fancy_insert(my_array, 'good') do |value|
value == 2
end
puts result.inspect
That way you can pass in an arbitrary block and value to be inserted.
Why not using the .each_with_index method:
arr = [1, 2, 3, 4, 5, 2, 3, 5]
arr.each_with_index {|e, i| arr.insert(i+1, "good") if e == 2}
Fast and furious.
Here's another way you can do it, using an Enumerator:
my_array = [1, 2, 3, 4, 2, 5, 15, 2]
enum = my_array.each
#=> #<Enumerator: [1, 2, 3, 4, 2, 5, 15, 2]:each>
my_array = []
loop do
x = enum.next
my_array << x
my_array << "good" if x == 2
end
my_array
#=> [1, 2, "good", 3, 4, 2, "good", 5, 15, 2, "good"]
Enumerator#next raises a StopInteration exception when the enumerator is already on the last element. Kernel#loop handles the exception by breaking the loop. That's why you will often see loop used when stepping through an enumerator.

Returning Multiple Values From Map

Is there a way to do:
a = b.map{ |e| #return multiple elements to be added to a }
Where rather than returning a single object for each iteration to be added to a, multiple objects can be returned.
I'm currently achieving this with:
a = []
b.map{ |e| a.concat([x,y,z]) }
Is there a way to this in a single line without having to declare a = [] up front?
Use Enumerable#flat_map
b = [0, 3, 6]
a = b.flat_map { |x| [x, x+1, x+2] }
a # => [0, 1, 2, 3, 4, 5, 6, 7, 8]
Use Enumerable#flat_map
Which is probably not much different than:
p [1, 2, 3].map{|num| [1, 2, 3]}.flatten
--output:-
[1, 2, 3, 1, 2, 3, 1, 2, 3]

In Ruby, is there a way to remove only 1 match in an Array easily?

In Ruby, the array subtraction or reject
>> [1,3,5,7,7] - [7]
=> [1, 3, 5]
>> [1,3,5,7,7].reject{|i| i == 7}
=> [1, 3, 5]
will remove all entries in the array. Is there an easy to remove just 1 occurrence?
>> a = [1,3,5,7,7]
>> a.slice!(a.index(7))
=> 7
>> a
=> [1,3,5,7]
The best I can think of is:
found = false
[1,3,5,7,7].reject{|i| found = true if !found && i == 7}
Or destructively:
arr = [1, 2, 3, 5, 7, 7]
arr.delete_at( arr.index(7))
arr #=> [1, 2, 3, 5, 7]
While it doesn't directly answer your question, uniq might be what you want.
[1,3,5,7,7].uniq # => [1,3,5,7]

ruby method chaining

var.split('/').delete_at(0)
upon inspection returns
""
no matter what the string, however....
var.split('/')
var.delete_at(0)
gives me no trouble. this is probably a stupid question, but are there some sort of restrictions/limitations regarding method chaining like this?
thanks,
brandon
The delete_at method deletes the element but returns the deleted element not the new array.
If you want to always return the object, you can use the tap method (available since Ruby 1.8.7).
a = [1, 2, 3, 4, 5, 6, 7]
a.delete_at(0) # => 1
a # => [2, 3, 4, 5, 6, 7]
a = [1, 2, 3, 4, 5, 6, 7]
a.tap { |a| a.delete_at(0) } # => returns [2, 3, 4, 5, 6, 7]
a # => [2, 3, 4, 5, 6, 7]
literally the first thing I tried to do was:
var.split('/').delete_at(0)
which upon inspection returned
""
no matter what the string
Are you sure? Try the string 'a/b':
irb(main):001:0> var = 'a/b'
=> "a/b"
irb(main):003:0> var.split('/').delete_at(0)
=> "a"
Note that the return value is the element deleted, not the array. The array which you created by performing the split was not stored anywhere and now you have no reference to it. You probably want to do this instead:
a = var.split('/')
a.delete_at(0)
If you always need to delete the first element, you can use other methods that return the object itself, such as slice!, for example:
s = 'foo/bar/baz'
#=> "foo/bar/baz"
s.split('/').slice!(1..-1)
#=> ["bar", "baz"]

Subtracting one Array from another in Ruby

I've got two arrays of Tasks - created and assigned.
I want to remove all assigned tasks from the array of created tasks.
Here's my working, but messy, code:
#assigned_tasks = #user.assigned_tasks
#created_tasks = #user.created_tasks
#Do not show created tasks assigned to self
#created_not_doing_tasks = Array.new
#created_tasks.each do |task|
unless #assigned_tasks.include?(task)
#created_not_doing_tasks << task
end
end
I'm sure there's a better way. What is it?
Thanks :-)
You can subtract arrays in Ruby:
[1,2,3,4,5] - [1,3,4] #=> [2,5]
ary - other_ary → new_ary Array Difference
Returns a new array that is a copy of the original array, removing any
items that also appear in other_ary. The order is preserved from the
original array.
It compares elements using their hash and eql? methods for efficiency.
[ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ]
If you need
set-like behavior, see the library class Set.
See the Array documentation.
The above solution
a - b
deletes all instances of elements in array b from array a.
[ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ]
In some cases, you want the result to be [1, 2, 3, 3, 5]. That is, you don't want to delete all duplicates, but only the elements individually.
You could achieve this by
class Array
def delete_elements_in(ary)
ary.each do |x|
if index = index(x)
delete_at(index)
end
end
end
end
test
irb(main):198:0> a = [ 1, 1, 2, 2, 3, 3, 4, 5 ]
=> [1, 1, 2, 2, 3, 3, 4, 5]
irb(main):199:0> b = [ 1, 2, 4 ]
=> [1, 2, 4]
irb(main):200:0> a.delete_elements_in(b)
=> [1, 2, 4]
irb(main):201:0> a
=> [1, 2, 3, 3, 5]
The code works even when the two arrays are not sorted. In the example, the arrays are sorted, but this is not required.

Resources