ruby sorting array - moving matched elements to the beginning - arrays

If I have an array: array = ["ruby", "code", "library"]. How can I move matched /^library$/ elements to the beginning. So array will look like this: array = ["library", "ruby", "code"]

it could be done in a number of ways. This is one
array = ["ruby", "code", "library"]
array.partition { |element| element.match /^library$/ }.flatten

Just out of curiosity:
[:select, :reject].map do |m|
["ruby", "code", "library"].public_send(m, &(/^library/.method(:=~)))
end.reduce :|

def move_to_front(arr, pattern)
mi = matching_indices(arr, pattern)
return arr unless mi
a = arr.dup
mi.reverse_each.with_object([]) { |i,b| b.unshift(a.delete_at(i)) }.concat(a)
end
def matching_indices(arr, pattern)
arr.each_index.select do |i|
case pattern
when Regexp then arr[i] =~ pattern
when Proc then pattern[arr[i]]
else (arr[i] == pattern)
end
end
end
move_to_front ["ruby", "code", "library"], /\Alibrary\z/
#=> ["library", "ruby", "code"]
move_to_front ["ruby", "library", "code", "library"], "library"
#=> ["library", "library", "ruby", "code"]
move_to_front ["ruby", "libraries", "code", "library"], /librar(?:ies|y)/
#=> ["libraries", "library", "ruby", "code"]
move_to_front ["ruby", "libraries", "code", "library"], /\Alibrar/
#=> ["libraries", "library", "ruby", "code"]
move_to_front ["ruby", "libraries", "code", "library"],
->(str) { str =~ /librar(?:ies|y)/ }
#=> ["libraries", "library", "ruby", "code"]
move_to_front ("1".."9").to_a, /[13579]/
#=> ["1", "3", "5", "7", "9", "2", "4", "6", "8"]
move_to_front ("1".."9").to_a, ->(n) { n.to_i.odd? }
#=> ["1", "3", "5", "7", "9", "2", "4", "6", "8"]
move_to_front ("1".."9").to_a, ->(n) { false }
#=> ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
move_to_front ("1".."9").to_a, ->(n) { true }
#=> ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
Note:
matching_indices ["ruby", "libraries", "code", "library"], /librar(?:ies|y)/
#=> [1, 3]
The method move_to_front preserves the order of those elements that are moved and those that are not moved.

Three for one cent.
array.inject([]){|a,e| e[/^library/] ? a.unshift(e) : a<<e}
and
array & ["library"] | array
In case array contains the search element multiple times it becomes
array.find_all{ |e| e[/^library/] } + array.reject{ |e| e[/^library/] }
If you hate to use the array variable twice it can also like this
[array].map{|a| a & ["library"] | a}.flatten
The last one: using grep
array.grep(/library/) + array.grep( /^(?!library)/)

Related

Replace all 'A' present in an Array with '15' in Swift 3

I want to replace all A characters present in an array with 15 using Swift 3.
Example array:
["4", "5", "6", "A", "A", "Q", "A"]
Desired result:
["4", "5", "6", "15", "15", "Q", "15"]
map to the rescue:
var a = ["4", "5", "6", "A", "A", "Q", "A"]
a = a.map({ $0 == "A" ? "15" : $0 })
print(a)// ["4", "5", "6", "15", "15", "Q", "15"]
EDIT: After error screenshot:
You have an array of characters and hence the above code is not working. Also, remember "15" is two characters and not one character. Hence, I have replaced character 'A' with string "15" and mapped it to an array of strings, instead
let player1 = "456AAQA"
var player1Cards = Array(player1.characters) // ["4", "5", "6", "A", "A", "Q", "A"]
var player1CardsStrings = player1Cards.map{$0 == "A" ? "15" : String($0)}
player1CardsStrings // ["4", "5", "6", "15", "15", "Q", "15"]
Tested on Playground.
Because your question is lacking information that you didn't gave at first, here is what you can do.
"for loop": You iterate and replace the value if needed.
That's a logic you could apply on almost all languages.
var array1 = ["4", "5", "6", "A", "A", "Q", "A"]
for index in 0 ... array1.count-1
{
if array1[index] == "A"
{
array1[index] = "15"
}
}
print("array1: \(array1)")
"for each loop": You iterate and replace the value if needed.
That's a logic you could apply on almost all languages (maybe less languages that the previous one)
var array2 = ["4", "5", "6", "A", "A", "Q", "A"]
for (index, object) in array2.enumerated()
{
if object == "A"
{
array2[index] = "15"
}
}
print("array2: \(array2)")
"map": the "map" iterate for you (here is the important part behind the magic), you check the value and replace the value if needed. The $0 represent the "current item".
Here is a specificity.
var array3 = ["4", "5", "6", "A", "A", "Q", "A"]
array3 = array3.map({
if $0 == "A"
{
return "15"
}
return $0
})
print("array3: \(array3)")
"map": the "map" iterate for you, you check the value and replace the value if needed with a ternary if test.
var array4 = ["4", "5", "6", "A", "A", "Q", "A"]
array4 = array4.map({$0 == "A" ? "15" : $0})
print("array4: \(array4)")
I'm gave 4 ways (I've could also have explicit more the map() with explicit closure, from the simplest to the more complicated. We can't know if you don't show your attempts where you are are stucked. Is it for loop? The basic algorithms?
Swift advanced user may be more fond of the last one, but for beginners, it's quite complex and "magic". So when they want to change it a little for a different test, they never know what to do.
Side note: I'm not a Swift developer, more an Objective-C, so it may be lacking of checks. This answers is to show different approaches, how you go from a "verbose" to a less "verbose" code, but that you need to master anyway. Even if you have issue with map(), you can "bypass" it and do it manually.
let values = ["4", "5", "6", "A", "A", "Q", "A"]
let mappedvalues = values.map({ (value: String) -> String in
if value != "A" {
return value
}
return "15"
})

Split string in ruby when character is different to previous character [duplicate]

This question already has answers here:
How to split a string of repeated characters with uneven amounts? Ruby
(2 answers)
Closed 5 years ago.
I would like to split a string when a character changes.
For example "aabbbc226%%*" should be split into an array like this ["aa", "bbb", "c", "22", "6", "%%", "*"]
Heres what I have right now
def split_when_char_change(str)
array = Array.new
chars = str.split('')
chars.each { |c|
array.push c
}
array
end
split_when_char_change("aabbbc226%%*")
I am getting this output: ["a", "a", "b", "b", "b", "c", "2", "2", "6", "%", "%", "*"] which is wrong.
How can I get my desired array?
Here is a one liner using chunk, map and join:
"aabbbc226%%*".chars.chunk(&:itself).map{|_,c| c.join}
# => ["aa", "bbb", "c", "22", "6", "%%", "*"]
"aabbbc226%%*".scan(/((.)\2*)/).to_h.keys
# => ["aa", "bbb", "c", "22", "6", "%%", "*"]
or
"aabbbc226%%*".scan(/((.)\2*)/).map(&:first)
# => ["aa", "bbb", "c", "22", "6", "%%", "*"]

Converting an array to hash in ruby

Supposed I have an array that looks like
testarr = [["Actor", "Morgan", "33", ["A","B"]],
["Movie", "Titanic", "44", ["A","A"]],
["Actor", "Jack Black", "333", ["A","A"]]]
I want to convert this into a hash which will be converted into a json eventually.
I want it to look like
{
"Actor" => {
{ "name" : "Morgan",
"Age" : 33",
"Films: { "A", "B" }} ,
{ "name" : "Jack Black",
"Age" : 44",
"Films: { "A", "A" }}
}
"Movie" => {
{ "Title" : "Titanic"
"Gross" : "44"
"Actors" : { "A", "A" }
}
}
Not sure about the exact format, but whatever makes sense.
I tried
def hashing(arr)
hash = Hash.new
arr.each do |item|
if item[0] == "Movie"
item.delete("Movie")
hash["Movie"] = item
item["Title"] = item[1]
item["Movie"]["Box Office"] = item[2]
item["Movie"]["Actors"] = item[3]
else
item.delete("Actor")
hash["Actor"] = item
item["Actor"]["Name"] == item[1]
item["Actor"]["Age"] == item[2]
item["Actor"]["Filmography"] == item[3]
end
end
return hash
end
testarr = [["Actor", "Morgan", "33", ["dsfds","dsfdsf"]],
["Movie", "Titanic", "44", ["dsfds","dfdsf"]],
["Actor", "Jack Black", "333", ["ssdsfds","dsfdsf"]]]
puts hashing(testarr)
But it gives me an error for putting the array item into "Movie" and "Actor" then trying to create keys like "Name" and "Age".
How can I make this as I desire?
testarr = [["Actor", "Morgan", "33", ["A","B"]],
["Movie", "Titanic", "44", ["A","A"]],
["Actor", "Jack Black", "333", ["A","A"]]]
a = Hash.new{ |h,k| h[k] = [] }
testarr.each do |arr|
b = {name: arr[1], age: arr[2], films: arr[3]}
a[arr[0]] << b
end
this will produce
{"Actor"=>[{"name"=>"Morgan", "age"=>"33", "films"=>["A", "B"]}, {"name"=>"Jack Black", "age"=>"333", "films"=>["A", "A"]}], "Movie"=>[{"name"=>"Titanic", "age"=>"44", "films"=>["A", "A"]}]}
Please try the below code,
v = [["Actor", "Morgan", "33", ["A", "B"]], ["Movie", "Titanic", "44", ["A", "A"]], ["Actor", "Jack Black", "333", ["A", "A"]]]
v.inject({}) do |ot, arr|
item = {name: arr[1], age: arr[2], films: arr[3]}
if ot[arr[0]].present?
ot[arr[0]] << item
else
ot[arr[0]] = []
ot[arr[0]] << item
end
ot
end
And the o/p is like below,
# => {"Actor"=>[{:name=>"Morgan", :age=>"33", :films=>["A", "B"]}, {:name=>"Jack Black", :age=>"333", :films=>["A", "A"]}], "Movie"=>[{:name=>"Titanic", :age=>"44", :films=>["A", "A"]}]}
Please note here the Actor is not hash of hashes, it's array of hashes, this is the standard way of keeping collection and convert it to json if you need by using to_json method.
You need to iterate through the array and parse each item, appending it to the resultant hash.
testarr = [["Actor", "Morgan", "33", ["A", "B"]],
["Movie", "Titanic", "44", ["A", "A"]],
["Actor", "Jack Black", "333", ["A", "A"]]]
results = {}
testarr.each do |item|
key, a, b, c = item
r = if key == 'Actor'
{ name: a, age: b, movies: c }
elsif key == 'Movie'
{ title: a, gross: b, actors: c }
end
results[key] = [] unless results[key]
results[key] << r
end
puts results
This will produce:
{"Actor"=>[{:name=>"Morgan", :age=>"33", :movies=>["A", "B"]}, {:name=>"Jack Black", :age=>"333", :movies=>["A", "A"]}], "Movie"=>[{:title=>"Titanic", :gross=>"44", :actors=>["A", "A"]}]}
The value in your :actor contains a hash without a key. The best thing you can do is put that into an array.
This will work. There might be a cleaner way, but I'm not sure how at the moment:
h = Hash.new { |hash, key| hash[key] = [] }
testarr = [["Actor", "Morgan", "33", ["A", "B"]], ["Movie", "Titanic", "44", ["A", "A"]], ["Actor", "Jack Black", "333", ["A", "A"]]]
testarr.each do |t|
if t[0] == 'Movie'
h[t[0]] << {title: t[1], gross: t[2], actors: t[3]}
else
h[t[0]] << {name: t[1], age: t[2], films: t[3]}
end
end
puts h
Output:
{"Actor"=>[{:name=>"Morgan", :age=>"33", :films=>["A", "B"]}, {:name=>"Jack Black", :age=>"333", :films=>["A", "A"]}], "Movie"=>[{:title=>"Titanic", :gross=>"44", :actors=>["A", "A"]}]}
I tried to keep the example you wrote.
First of all, it must be shaped for Array(such as [a, b] ) not Hash( {a, b} ) list of items
# You may want result like this ...
{
"Actor": [ # not '{' but '['
{
"name": "Morgan",
"Age": "33",
"Films": ["A", "B"] # not '{' but '[' also
},
{
"name": "Jack Black",
"Age": "44",
"Films": ["A", "A"]
}
],
"Movie": [
{
"Title": "Titanic",
"Gross": "44",
"Actors": ["A", "A"]
}
]
}
and then your function should be like this ...
def hashing(arr)
hash = Hash.new
hash["Movie"], hash["Actor"] = [], []
arr.each do |item|
if item[0] == "Movie"
movie = {}
movie["Title"] = item[1]
movie["Box Office"] = item[2]
movie["Actors"] = item[3]
item.delete("Movie") # optional
hash["Movie"] << movie
else
actor = {}
actor["Name"] = item[1]
actor["Age"] = item[2]
actor["Filmography"] = item[3]
item.delete("Actor") # optional
hash["Actor"] << actor
end
end
return hash
end
Then it's time to test!
as your codes,
testarr = [
["Actor", "Morgan", "33", ["dsfds","dsfdsf"]],
["Movie", "Titanic", "44", ["dsfds","dfdsf"]],
["Actor", "Jack Black", "333", ["ssdsfds","dsfdsf"]]
]
puts hashing(testarr)
It will return this:
{
"Movie"=>
[
{"Title"=>"Titanic", "Box Office"=>"44", "Actors"=>["dsfds", "dfdsf"]}
],
"Actor"=>
[
{"Name"=>"Morgan", "Age"=>"33", "Filmography"=>["dsfds", "dsfdsf"]},
{"Name"=>"Jack Black", "Age"=>"333", "Filmography"=>["ssdsfds", "dsfdsf"]}
]
}
Code
def convert(arr, keys)
arr.group_by(&:first).transform_values do |a|
a.map { |key, *values| keys[key].zip(values).to_h }
end
end
Example (using testarr defined in the question)
keys = { "Actor"=>[:name, :Age, :Films], "Movie"=>[:Title, :Gross, :Actors] }
convert(testarr, keys)
#=> { "Actor"=>[
# {:name=>"Morgan", :Age=>"33", :Films=>["A", "B"]},
# {:name=>"Jack Black", :Age=>"333", :Films=>["A", "A"]}
# ],
# "Movie"=>[
# {:Title=>"Titanic", :Gross=>"44", :Actors=>["A", "A"]}
# ]
# }
Explanation
See Enumerable#group_by, Hash#transform_values, Array#zip and Array#to_h.
The steps are as follows.
h = testarr.group_by(&:first)
#=> { "Actor"=>[
# ["Actor", "Morgan", "33", ["A", "B"]],
# ["Actor", "Jack Black", "333", ["A", "A"]]
# ],
# "Movie"=>[
# ["Movie", "Titanic", "44", ["A", "A"]]
# ]
# }
Though not quite equivalent, you can think of testarr.group_by(&:first) as being "shorthand" for testarr.group_by { |a| a.first }. Continuing,
e0 = h.transform_values
#=> #<Enumerator:
# {"Actor"=>[["Actor", "Morgan", "33", ["A", "B"]],
# ["Actor", "Jack Black", "333", ["A", "A"]]],
# "Movie"=>[["Movie", "Titanic", "44", ["A", "A"]]]}
# :transform_values>
The first element is generated by the enumerator e0, passed to the block and the block variable is set equal to that value.
a = e0.next
#=> [["Actor", "Morgan", "33", ["A", "B"]],
# ["Actor", "Jack Black", "333", ["A", "A"]]]
A second enumerator is now created.
e1 = a.map
#=> #<Enumerator: [["Actor", "Morgan", "33", ["A", "B"]],
# ["Actor", "Jack Black", "333", ["A", "A"]]]:map>
The first value is generated by e1, passed to the inner block and the block variables are assigned values (using disambiguation).
key, *values = e1.next
#=> ["Actor", "Morgan", "33", ["A", "B"]]
key
#=> "Actor"
values
#=> ["Morgan", "33", ["A", "B"]]
The inner block calculation is now performed.
b = keys[key].zip(values)
#=> keys["Actor"].zip(["Morgan", "33", ["A", "B"]])
#=> [:name, :Age, :Films].zip(["Morgan", "33", ["A", "B"]])
#=> [[:name, "Morgan"], [:Age, "33"], [:Films, ["A", "B"]]]
b.to_h
#=> {:name=>"Morgan", :Age=>"33", :Films=>["A", "B"]}
Now the second and last element is generated by e1 and the same calculations are performed.
key, *values = e1.next
#=> ["Actor", "Jack Black", "333", ["A", "A"]]
b = keys[key].zip(values)
#=> [[:name, "Jack Black"], [:Age, "333"], [:Films, ["A", "A"]]]
b.to_h
#=> {:name=>"Jack Black", :Age=>"333", :Films=>["A", "A"]}
When another value is sought from e1 we obtain the following.
e1.next
#=> StopIteration: iteration reached an end
This exception is caught, causing e1 to return to the outer block. At this point e0 generates it next (and last value).
a = e0.next
#=> [["Movie", "Titanic", "44", ["A", "A"]]]
The remaining calculations are similar.

Sort more than 10 numbers in Swift3

I try to sort a String Array with numbers but i dont get the right order.
print(alleTouren) // ["1", "3", "2", "5", "15", "4"]
alleTouren = alleTouren.sorted(by: {$0 < $1})
print(alleTouren) // ["1", "15", "2", "3", "4", "5"]
I also tried alleTouren.sort(by:<) and alleTouren.sort() but i always get back the 15 too early. What i am doing wrong?
Since all strings can be converted to Int add the conversion to the closure.
var alleTouren = ["1", "3", "2", "5", "15", "4"]
alleTouren = alleTouren.sorted(by: { Int($0)! < Int($1)! })
Alternatively use the compare function of String with numeric option which is probably more efficient.
alleTouren = alleTouren.sorted(by: { $0.compare($1, options:.numeric) == .orderedAscending} )
The problem is that you seem to be saying you want to sort them as though they are numbers, but they are strings, so "1", "15", "2"... is correct. You could try converting $0 and $1 to integers and comparing these.
I'm not a Swift expert, but this seems to work:
alleTouren = alleTouren.sorted{let s0 = Int($0)
let s1 = Int($1)
return s0! < s1!}

Map an array without losing unchanged elements

I split the following string:
str = "0001110010101000011111100001110010000010000000011101011100001"
into an array of grouped "0"s and "1"s:
str_arr = []
str.scan(/((.)\2*)/) { |x| str_arr.push(x[0]) }
str_arr # => ["000", "111", "00", "1", "0", "1", "0", "1", "0000", "111111", "0000", "111", "00", "1", "00000", "1", "00000000", "111", "0", "1", "0", "111", "0000", "1"]
I want to replace elements which contain the character "1" and have a length of less than 3 characters with the character ".", but retain the other elements.
I reached this far in my code:
str_arr.map!{|x| if x.include?("1") && x.length < 3; x = "." end}
str_arr # => [nil, nil, nil, ".", nil, ".", nil, ".", nil, nil, nil, nil, nil, ".", nil, ".", nil, nil, nil, ".", nil, nil, nil, "."]
The last step is where I trip up. I converted the elements I wanted to to ".", but converted the remaining elements to nil.
Is there a way to replace the elements I want to change without affecting the remaining elements?
The reason they are getting converted is because if statements in ruby return nil if the condition wasn't met:
if false; end # => nil
To fix this, just use the ternary operator (?:) in place of if then end and provide the original value as alternative:
str_arr.map!{ |x| x.include?('1') && x.length < 3 ? '.' : x }
Bonus:
A simpler regex, which will do the same:
0+|1+
Using each_index would be appropriate in this case.
str_arr.each_index{|i| str_arr[i] = "." if str_arr[i] =~ /\A1{,2}\z/}
# => ["000", "111", "00", ".", "0", ".", "0", ".", "0000", "111111", "0000", "111",
# "00", ".", "00000", ".", "00000000", "111", "0", ".", "0", "111", "0000", "."]

Resources