Merging arrays without losing original order, ruby - ruby

I have an array structure that looks like:
a=[
[['a','A'],['b','B'],['c','C']],
[['d','D'],['e','E'],['f','F']]
]
How to merge inner two arrays so the new structure will be Array of arrays
[
['a','A'],['b','B'],['c','C'],['d','D'],['e','E'],['f','F']
]
Tried
a.inject([]){|k,v| v | k} # but order gets changed
=> [["d", "D"], ["e", "E"], ["f", "F"], ["a", "A"], ["b", "B"], ["c", "C"]]
How can i get desired result without loosing the order.
Tips, comments, suggestions, please?
Thnx.

array.flatten takes a parameter:
a.flatten(1) #[["a", "A"], ["b", "B"], ["c", "C"], ["d", "D"], ["e", "E"], ["f", "F"]]

Try this:
a.inject([]){|k,v| k|v}

Related

Add elements of an array to a specific index of each array within an array of arrays

I have an array of arrays that serves as a table of data, and am trying to add an extra array as though adding an extra column to the table.
For simplicity, suppose the first array is a
a = [["a", "b", "c"], ["e", "f", "g"], ["i", "j", "k"]]
and the second array is b
b = ["d", "h", "l"]
the desired output is:
c = [["a", "b", "c", "d"], ["e", "f", "g", "h"], ["i", "j", "k", "l"]]
I have tried using + and some attempts at using map but cannot get it
You can zip them together which will create array elements like [["a", "b", "c"], "d"] and then just flatten each element.
a.zip(b).map(&:flatten)
#=> [["a", "b", "c", "d"], ["e", "f", "g", "h"], ["i", "j", "k", "l"]]
Answer improved as per Cary's comment. I think he's done Ruby stuff before.
a.zip(b).map { |arr,e| arr + [e] }
#=> [["a", "b", "c", "d"],
# ["e", "f", "g", "h"],
# ["i", "j", "k", "l"]]
The intermediate calculation is as follows.
a.zip(b)
#=> [[["a", "b", "c"], "d"],
# [["e", "f", "g"], "h"],
# [["i", "j", "k"], "l"]]
See Array#zip.
You can use #each_with_index combined with #map to iterate over the array a and append respective elements of array b
> a.each_with_index.map{|e, i| e | [b[i]] }
=> [["a", "b", "c", "d"], ["e", "f", "g", "h"], ["i", "j", "k", "l"]]

How to make all possible pairs from an array

a = ["a","b","c"]
a.each_cons(2).to_a # => [["a", "b"], ["b", "c"]]
I want three possible pairs from this array
You can use Array#permutation to generate sub-arrays with all combination.
a.permutation(2).to_a
# => [["a", "b"], ["a", "c"], ["b", "a"], ["b", "c"], ["c", "a"], ["c", "b"]]
Following this you can pick 3 random arrays using Array#sample (assuming you want to pick random sub-arrays). Here:
a.permutation(2).to_a.sample(3)
# => [["c", "b"], ["c", "a"], ["b", "c"]]
Try this:
a = ["a","b","c"]
a.permutation(2).to_a.take(3)
# => [["a", "b"], ["a", "c"], ["b", "a"]]
a = ["a","b","c"]
a.combination(2).to_a # => [["a", "b"], ["a", "c"], ["b", "c"]]

Convert multiple level array to single level array?

I want to convert this array
[[["b", "c"], ["c", "d"]], [["v", "e"], ["r", "g"]]]
into
[["b", "c"], ["c", "d"], ["v", "e"], ["r", "g"]]
How can I convert this ?
Array#flatten takes an optional level:
The optional level argument determines the level of recursion to flatten
Example:
[[["b", "c"], ["c", "d"]], [["v", "e"], ["r", "g"]]].flatten(1)
#=> => [["b", "c"], ["c", "d"], ["v", "e"], ["r", "g"]]
arr = []
a = [[["b", "c"], ["c", "d"]], [["v", "e"], ["r", "g"]]]
a.map{|x| x.map{|y| arr << y}}
puts arr

delete similar elements in an array in ruby

I have the following array
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"]]
I want to get the following output [["a","b"], ["a","c"], ["b","c"]] and delete ["b","a"] and ["c","b"]
I have the following code
a3.each do |ary3|
x = ary3[0]
y = ary3[1]
x = ary3[1]
y = ary3[0]
if a3.include?([x,y])
a3 - [y,x]
end
end
print a3
I tried using the swap, but no luck!
Thanks for the help.
Two arrays are considered to be equal if they contain the same elements and these elements are in the same order:
["a", "b"] == ["b", "a"]
#=> false
["a", "b"] == ["a", "b"]
#=> true
So you need to sort the inner arrays first and then you can use Array#uniq to ensure that each element in the outer array will only appear once:
arr = [["a", "b"], ["a", "c"], ["b", "c"], ["b", "a"], ["c", "b"]]
arr.map(&:sort).uniq
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
This will leave arr untouched, however:
arr
#=> [["a", "b"], ["a", "c"], ["b", "c"], ["b", "a"], ["c", "b"]]
You will need to use mutator methods (with a !) to edit the array in place:
arr = [["a", "b"], ["a", "c"], ["b", "c"], ["b", "a"], ["c", "b"]]
arr.map!(&:sort).uniq!
arr
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
Edit
As a follow-up to #sawa's comment, who was concerned that it may not be desirable to change the ordering of the inner arrays, i looked a bit deeper into Array#uniq. Consider the following array:
arr = [["b", "a"], ["a", "c"], ["b", "c"], ["b", "a"], ["c", "b"]]
I figured out that Array#uniq actually takes a block that lets you specify how the elements should be be compared:
arr.uniq!{|x| x.sort }
arr
#=> [["b", "a"], ["a", "c"], ["b", "c"]]
Cool thing is, this also works with Symbol#to_proc (the &: notation) and actually looks even more elegant than my original answer:
arr.uniq!(&:sort)
arr
#=> [["b", "a"], ["a", "c"], ["b", "c"]]
You can still use Array#sort! if you want the inner arrays to be sorted afterwards:
arr.uniq!(&:sort!)
arr
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
My last observation on this is though, that the order probably isn't important or else two arrays with different order would not be considered equal. This got me thinking (again) and i posed myself the question: Why not use a Set? It would work like this:
require 'set'
sets = [Set["a", "b"], Set["a", "c"], Set["b", "c"], Set["b", "a"], Set["c", "b"]]
sets.uniq!
sets
#=> [#<Set: {"a", "b"}>, #<Set: {"a", "c"}>, #<Set: {"b", "c"}>]
Just keep in mind that a Set will not allow you to add the same element multiple times, whereas an array does:
[%w[a b b b c], %w[a b b b c], %w[a b c]].uniq(&:sort)
#=> [["a", "b", "b", "b", "c"], ["a", "b", "c"]]
[Set.new(%w[a b b b c]), Set.new(%w[a b b b c]), Set.new(%w[a b c])].uniq
#=> [#<Set: {"a", "b", "c"}>]
check this one: #delete_if as below:
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"]]
p a3.delete_if{|x| [["b", "a"], ["c","b"]].include? x}
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
As per your comment and description post :
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"],["b", "a"]]
p a3.each {|x| a3.delete(x.reverse) if a3.include? x.reverse}
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
BenchMark:
require 'benchmark'
N = 10000
Benchmark.bm(20) do | x |
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"],["b", "a"]]
x.report('Mine') do
N.times { a3.each {|x| a3.delete(x.reverse) if a3.include? x.reverse} }
end
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"],["b", "a"]]
x.report('padde') do
N.times { a3.uniq!(&:sort!) }
end
end
Output:
user system total real
Mine 0.172000 0.000000 0.172000 ( 0.361021)
padde 0.203000 0.000000 0.203000 ( 0.460026)

Why does using a regex and .scan produce these results?

>> "aaaaaafbfbfsjjseew".scan(/(.)/)
=> [["a"], ["a"], ["a"], ["a"], ["a"], ["a"], ["f"], ["b"], ["f"], ["b"], ["f"], ["s"], ["j"], ["j"], ["s"], ["e"], ["e"], ["w"]]
>> "aaaaaafbfbfsjjseew".scan(/((.))/)
=> [["a", "a"], ["a", "a"], ["a", "a"], ["a", "a"], ["a", "a"], ["a", "a"], ["f", "f"], ["b", "b"], ["f", "f"], ["b", "b"], ["f", "f"], ["s", "s"], ["j", "j"], ["j", "j"], ["s", "s"], ["e", "e"], ["e", "e"], ["w", "w"]]
>> "aaaaaafbfbfsjjseew".scan(/((.)\2*)/)
=> [["aaaaaa", "a"], ["f", "f"], ["b", "b"], ["f", "f"], ["b", "b"], ["f", "f"], ["s", "s"], ["jj", "j"], ["s", "s"], ["ee", "e"], ["w", "w"]]
>> "aaaaaafbfbfsjjseew".scan(/((.)\1*)/)
=> [["a", "a"], ["a", "a"], ["a", "a"], ["a", "a"], ["a", "a"], ["a", "a"], ["f", "f"], ["b", "b"], ["f", "f"], ["b", "b"], ["f", "f"], ["s", "s"], ["j", "j"], ["j", "j"], ["s", "s"], ["e", "e"], ["e", "e"], ["w", "w"]]
>> "aaaaaafbfbfsjjseew".scan(/((.)\3*)/)
=> [["a", "a"], ["a", "a"], ["a", "a"], ["a", "a"], ["a", "a"], ["a", "a"], ["f", "f"], ["b", "b"], ["f", "f"], ["b", "b"], ["f", "f"], ["s", "s"], ["j", "j"], ["j", "j"], ["s", "s"], ["e", "e"], ["e", "e"], ["w", "w"]]
From the fine manual:
str.scan(pattern) → array
[...]
If the pattern contains groups, each individual result is itself an array containing one entry per group.
This one:
"aaaaaafbfbfsjjseew".scan(/(.)/)
has a group so you get an array of arrays: each individual result is a single element array.
The next one:
"aaaaaafbfbfsjjseew".scan(/((.))/)
has two groups which happen to have the same value so you get two identical elements in your individual result arrays.
The third one:
"aaaaaafbfbfsjjseew".scan(/((.)\2*)/)
again contains two groups but also contains a back-reference to the inner group so the outer group (AKA the first group) gobbles up duplicates and you get ["aaaaaa", "a"], ["jj", "j"], and ["ee", "e"].
The fourth one:
"aaaaaafbfbfsjjseew".scan(/((.)\1*)/)
just tries to switch the back-reference to the outer group but \1 isn't defined inside group 1 so it is equivalent to /((.))/.
The fifth one:
"aaaaaafbfbfsjjseew".scan(/((.)\3*)/)
tries to refer to a non-existant group (group 3 when there are only two groups) so it behaves the same as /((.))/.
"aaaaaafbfbfsjjseew".scan(/(.)/) means the string can be splitted into individual array of strings.
Here parenthesis tells that it's an array, and the .-symbol in parenthesis represents number of characters in that individual string of array.
If we write for suppose "hellovenkat".scan(/(...)/) , this results
[["hel"],["lov"],["enk"]]. It doesn't give the last index, because it can not contain three characters.
If we give "hello venkat".scan(/(...)/), this results as following.
Ans: [["hel"], ["lo "], ["ven"], ["kat"]].

Resources