Ruby each_slice with different sizes - arrays

I have:
stuff = [1, 2, "a", "b", "c", "d", 4, 5, "z", "l", "m", "l", 5, 4, 4, 77]
Numbers come in groups of multiples of two, and letters come in groups of multiples of four.
I want to group numbers in twos and letters in fours like this:
stuff_processed = [
[1, 2],
["a", "b", "c", "d"],
[4, 5],
["z", "l", "m", "l"],
[5, 4],
[4, 77]
]
The order inside of an array that holds numbers or letters is important, the order between the arrays I do not care about.
I know stuff.each_slice(2).to_a will take me part of the way. I can't figure out how to get all the way to what I need though.

stuff
.chunk(&:class)
.flat_map{|klass, a| a.each_slice(klass == Fixnum ? 2 : 4).to_a}
# => [[1, 2], ["a", "b", "c", "d"], [4, 5], ["z", "l", "m", "l"], [5, 4], [4, 77]]

arr = [1, 2, "a", "b", "c", "d", 4, 5, "z", "l", "m", "l", "s", "t",
"u", "v", 5, 4, 4, 77, 91, 65]
H = { Fixnum=>1, String=>3 }
count = 0
arr.slice_when do |a,b|
if a.class == b.class && count < H[a.class]
count += 1
false
else
count = 0
true
end
end.to_a
# => [[1, 2], ["a", "b", "c", "d"], [4, 5], ["z", "l", "m", "l"],
# ["s", "t", "u", "v"], [5, 4], [4, 77], [91, 65]]
See Enumerable#slice_when, which first appeared in Ruby v2.2.

This Array#conditional_slice method accepts a Block and returns an Enumerator :
stuff = [1, 2, "a", "b", "c", "d", 4, 5, "z", "l", "m", "l", 5, 4, 4, 77]
class Array
def conditional_slice(&block)
clone = self.dup
Enumerator.new do |yielder|
until clone.empty? do
yielder << clone.shift(block_given? ? block.call(clone.first) : 1)
end
end
end
end
sliced_stuff = stuff.conditional_slice{|x| x.is_a?(Numeric) ? 2 : 4}
puts sliced_stuff.to_a.inspect
# => [[1, 2], ["a", "b", "c", "d"], [4, 5], ["z", "l", "m", "l"], [5, 4], [4, 77]]

Related

How to get ALL combinations of array elements in Ruby?

If I have an array:
%w(a b c d e)
=> ["a","b","c","d","e"]
I can get some combinations with
irb(main):071:0> %w(a b c d e).combination(3).to_a
=> [["a", "b", "c"], ["a", "b", "d"], ["a", "b", "e"], ["a", "c", "d"], ["a", "c", "e"], ["a", "d", "e"], ["b", "c", "d"], ["b", "c", "e"], ["b", "d", "e"], ["c", "d", "e"]]
However that is not ALL the combinations, just the unique ones, e.g. ["e", "a", "b"] is missing
When I similarly try with s smaller array I only get one result:
irb(main):059:0> %w(a b c).combination(3).to_a
=> [["a", "b", "c"]]
How can I get all 6 combinations, i.e. for ['a', 'b', 'c'] I want to get
[['a','b','c'], ['a','c','b'], ['b', 'a', 'c'], ['b', 'c', 'a'], ['c', 'a', 'b'], ['c', 'b', 'a']
Similarly for [1,2,3,4] if I want all the 3 digit combo I should get
irb(main):074:0> [[1,2,3],[1,2,4],[1,3,2],[1,3,4],[1,4,2],[1,4,3], [2,1,3],[2,1,4],[2,3,4],[2,3,1],[2,4,1],[2,4,2], [3,1,2],[3,1,4],[3,2,3],[3,2,4],[3,4,2],[3,4,1]]
?
You are looking for permutation instead of combination.
In combinations, we do not care about the order of the elements, and only care about the presence of all the elements in the set.
[1,2,3,4].permutation(3).to_a
#=> [[1, 2, 3], [1, 2, 4], [1, 3, 2], [1, 3, 4], [1, 4, 2], [1, 4, 3], [2, 1, 3], [2, 1, 4], [2, 3, 1], [2, 3, 4], [2, 4, 1], [2, 4, 3], [3, 1, 2], [3, 1, 4], [3, 2, 1], [3, 2, 4], [3, 4, 1], [3, 4, 2], [4, 1, 2], [4, 1, 3], [4, 2, 1], [4, 2, 3], [4, 3, 1], [4, 3, 2]]

Insert into a nested array

I have cars described in a file in this form:
CAR
Audi
1x1
XXX
XXXXX
_X
_X
I want to insert the cars from this file into a 10 x 10 nested array.
The second line are coordinates (R x C) from the left top point for the start of the car. Third to fifth lines show the shape of the car.
OUTPUT= [["O","O","O","O","O","O","O","O","O","O"],
["O","X","X","X","O","O","O","O","O","O"],
["O","X","X","X","X","X","O","O","O","O"],
["O","O","X","O","O","O","O","O","O","O"],
["O","O","X","O","O","O","O","O","O","O"]
["O","O","O","O","O","O","O","O","O","O"]
["O","O","O","O","O","O","O","O","O","O"]
["O","O","O","O","O","O","O","O","O","O"]
["O","O","O","O","O","O","O","O","O","O"]
["O","O","O","O","O","O","O","O","O","O"]
INPUT arr =[]
This should work, pretty tricky. Sure there is a better way.
First map the file into a hash.
car_array = []
File.open('car_array.txt').each_with_index do |line, idx|
car_array << line.chomp
end
p car_array
# => ["CAR", "Audi", "1x1", "XXX", "XXXXX", "_X", "_X", "CAR", "BMW", "5x1", "__X", "XXX", "X", "XX"]
car_hash = {}
key = nil
car_array.each_with_index do |e, index|
key = car_array[index-1].downcase.to_sym if car_array[index-2] == "CAR"
car_hash[key] = [e] if car_array[index-2] == "CAR"
car_hash[key] << e if car_hash[key] unless (car_array[index-2] == "CAR" || car_array[index-1] == "CAR" || car_array[index] == "CAR")
end
p car_hash
# => {:audi=>["1x1", "XXX", "XXXXX", "_X", "_X"], :bmw=>["5x1", "__X", "XXX", "X", "XX"]}
car_hash.transform_values do |array|
h = {start: [], points: []}
h[:start] = array[0].split('x').map(&:to_i).map!{ |e| e}
(array.size - 1).times do |n|
h[:points] << array[n+1]
end
array[0] = h
(array.size - 1).times do |n|
array.pop
end
end
car_hash.transform_values! {|v| v[0]}
p car_hash
# => {:audi=>{:start=>[1, 1], :points=>["XXX", "XXXXX", "_X", "_X"]}, :bmw=>{:start=>[5, 1], :points=>["__X", "XXX", "X", "XX"]}}
car_hash.each_value do |car|
car[:points].map! do |point|
point.scan(/./)
end
row = car[:start][0] - 1
col = car[:start][1] - 1
car[:points].map! do |points|
delta_col = 0
row += 1
points.map! do |point|
delta_col +=1
point == 'X' ? [row, col + delta_col] : nil
end
points.compact
end
car[:points] = car[:points].flatten(1)
end
p car_hash
# => {:audi=>{:start=>[1, 1], :points=>[[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5], [3, 2], [4, 2]]}, :bmw=>{:start=>[5, 1], :points=>[[5, 3], [6, 1], [6, 2], [6, 3], [7, 1], [8, 1], [8, 2]]}}
Then us the hash to build the table.
table_model = Array.new(10) {Array.new(10,'O')}
def print_table (table)
table.each {|row| p row}
end
audi_table = table_model.dup
car_hash[:audi][:points].each { |point| audi_table[point[0]][point[1]] = 'x' }
print_table audi_table
# => ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "x", "x", "x", "O", "O", "O", "O", "O", "O"]
# => ["O", "x", "x", "x", "x", "x", "O", "O", "O", "O"]
# => ["O", "O", "x", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "O", "x", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O"]
# => ["O", "O", "O", "O", "O", "O", "O", "O", "O", "O"]

Find all combination of string array in swift

I have an Array of String and I want to find all the possible combinations of its element
For Example :
Array = [A,B,C,D]
should produce result as :
[A,AB,AC,AD,ABC,ABD,ACD,ABCD,B,BC,BD,BCD,C,CD,D]
Here is my Logic :
var array = ["A", "B", "C","D"]
var list = [String]()
for i in 0..<array.count{
let c = array[i]
list.append(c)
var d = c
for count in 1..<array.count{
if i+count < array.count{
for j in i+count..<array.count{
var a = d
a.appendContentsOf(array[j])
print("a : \(a)")
list.append(a)
}
}
d = c
d.appendContentsOf(array[count])
print("d : \(d)")
}
}
print(list.description)
Its Output is :
["A", "AB", "AC", "AD", "ABC", "ABD", "ACD", "B", "BC", "BD", "BBD",
"C", "CD", "D"]
This output is missing ABCD and wrongly printed BCD as BBD
Anyone Please Help me in this by Enhancing my code or suggesting your own logic for this.
#yannick's answer is very close.
By computing a Power Set of your set, you obtain all the possible subsets (including your original set and the empty set).
Once you have obtained the Power Set, all you have to do is join the subsets into a single string in order to obtain the result that you're looking for.
Here's the complete solution (along with updated code and plenty of comments):
extension Array {
var powerset: [[Element]] {
guard count > 0 else {
return [[]]
}
// tail contains the whole array BUT the first element
let tail = Array(self[1..<endIndex])
// head contains only the first element
let head = self[0]
// computing the tail's powerset
let withoutHead = tail.powerset
// mergin the head with the tail's powerset
let withHead = withoutHead.map { $0 + [head] }
// returning the tail's powerset and the just computed withHead array
return withHead + withoutHead
}
}
let myArray = ["A", "B", "C", "D"]
print(myArray.powerset) // -> [["D", "C", "B", "A"], ["C", "B", "A"], ["D", "B", "A"], ["B", "A"], ["D", "C", "A"], ["C", "A"], ["D", "A"], ["A"], ["D", "C", "B"], ["C", "B"], ["D", "B"], ["B"], ["D", "C"], ["C"], ["D"], []]
// joining the subsets
let myResult = myArray.powerset.map { $0.sort().joinWithSeparator("") }
print(myResult) // -> ["A", "AB", "ABC", "ABCD", "ABD", "AC", "ACD", "AD", "B", "BC", "BCD", "BD", "C", "CD", "D", ""]
PS
Note that this solution uses a recursive approach, while yours was using an iterative approach.
PPS
If you don't want the empty string "" in your solution, you can just filter it away:
let myResult = myArray.powerset.map({ $0.sort().joinWithSeparator("") }).filter({ $0 != "" })
print(myResult) // -> ["A", "AB", "ABC", "ABCD", "ABD", "AC", "ACD", "AD", "B", "BC", "BCD", "BD", "C", "CD", "D"]
It looks like you want to have the Power set of your array.
In mathematics, the power set (or powerset) of any set S is the set of
all subsets of S, including the empty set and S itself.
I found this Code on GitHub.
extension Array {
var powerset: [[Element]] {
if count == 0 {
return [self]
}
else {
let tail = Array(self[1..<endIndex])
let head = self[0]
let withoutHead = tail.powerset
let withHead = withoutHead.map { $0 + [head] }
return withHead + withoutHead
}
}
}
println([1,2,3,4].powerset) -> [[4, 3, 2, 1], [3, 2, 1], [4, 2, 1], [2, 1], [4, 3, 1], [3, 1], [4, 1], [1], [4, 3, 2], [3, 2], [4, 2], [2], [4, 3], [3], [4], []]
I find a neater answer for it.Power set of Collection.
The principle is using induction on the size of a collection, as showed on that link.
Here is the copy of code from that link. And all credits to its author.
extension Collection {
public var powerSet: [[Element]] {
guard let fisrt = self.first else {return [[]]}
return self.dropFirst().powerSet.flatMap{[$0, [fisrt] + $0]}
}
}
let s: Set<Int> = [1,2,3]
s.powerSet //[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
let a: Array<Int> = [1,2,3]
a.powerSet //[[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
I know some good answers have been given already, but coming from a Java background, I just wanted to drop some insights using bitwise operators (which surprisingly still work in Swift).
You can try this out:
let len = stringArr.count
for i in 0 ..< (1<<len){
print("{", terminator: "")
for j in 0 ..< len {
if ((i & (1<<j)) > 0) {
print(stringArr[j], terminator: "")
}
}
print("}")
}
You can find more information on bitwise operators here
I will take a shot also using this logic as reference:
extension RangeReplaceableCollection {
var subSets : [SubSequence] {
guard !isEmpty else { return [] }
let count = self.count
let n = 1 << count - 1
var subSequences: [SubSequence] = .init(repeating: SubSequence(), count: n-1)
(0 ..< n).forEach { x in
autoreleasepool {
var counter = 0
for element in self {
if x & 1 << counter > 0 {
subSequences[x-1].append(element)
}
counter += 1
}
}
}
return subSequences + [self[...]]
}
}
Playground Testing:
["A", "B", "C","D"].subSets // [["A"], ["B"], ["A", "B"], ["C"], ["A", "C"], ["B", "C"], ["A", "B", "C"], ["D"], ["A", "D"], ["B", "D"], ["A", "B", "D"], ["C", "D"], ["A", "C", "D"], ["B", "C", "D"], ["A", "B", "C", "D"]]
"ABCD".subSets // ["A", "B", "AB", "C", "AC", "BC", "ABC", "D", "AD", "BD", "ABD", "CD", "ACD", "BCD", "ABCD"]

sort a array according to another array, swift

I am wondering is there any build-in function I can sort a array according to another array. For example: sort testStringArray according to testIntArray
var testStringArray = ["a", "b", "c", "d", "e"]
var testIntArray = [21, 3, 43, 5, 1]
After the function, testStringArray will be
testIntArray.sort // [1, 3, 5, 21, 43]
testStringArray // ["e", "b", "d", "a", "c"]
var array1 = ["a", "b", "c", "d", "e"]
var array2 = [21, 3, 43, 5, 1]
let sorted = zip(array1, array2).sort { $0.1 < $1.1 }
array1 = sorted.map { $0.0 }
array2 = sorted.map { $0.1 }
print(array1) // ["e", "b", "d", "a", "c"]
print(array2) // [1, 3, 5, 21, 43]
Something like this? I feel like it can be done better...
EDIT:
This doesn't feel like it's much better...
zip(array1, array2).sort { $0.1 < $1.1 }.forEach {
array1.removeAtIndex(array1.indexOf($0.0)!)
array1.insert($0.0, atIndex: array1.count)
array2.removeAtIndex(array2.indexOf($0.1)!)
array2.insert($0.1, atIndex: array2.count)
}
print(array1) // ["e", "b", "d", "a", "c"]
print(array2) // [1, 3, 5, 21, 43]

How do you sort two arrays exactly the same?

How would you go about sorting two arrays the same way?
hey = %w[e c f a d b g]
hoo = [1,2,3,4,5,6,7]
hey.sort #=> [a,b,c,d,e,f,g]
hoo.same_sort #=> [4,6,2,5,1,3,7]
Have a try:
hey.zip(hoo).sort
=> [["a", 4], ["b", 6], ["c", 2], ["d", 5], ["e", 1], ["f", 3], ["g", 7]]
hey.zip(hoo).sort.transpose
=> [["a", "b", "c", "d", "e", "f", "g"], [4, 6, 2, 5, 1, 3, 7]]
You can do this with a single sort, using Enumerable#sort_by and Array#values_at:
sorted_indices = hey.each_index.sort_by { |i| hey[i] }
#=> [3, 5, 1, 4, 0, 2, 6]
hey.values_at(*sorted_indices)
#=> ["a", "b", "c", "d", "e", "f", "g"]
hoo.values_at(*sorted_indices)
#=> [4, 6, 2, 5, 1, 3, 7]

Resources