Swift keep array order whilst filling it - arrays

So, I have a 2D array called allBusinesses of type BusinessClass. I fill this array in the following way:
allBusinesses[0].append(contentsOf: [B11, B12, B13, B14, B15, B16])
allBusinesses[1].append(contentsOf: [B21, B22, B23, B24, B25, B26])
Where B11, B12 ... B26 are all BusinessClass instances.
I have another 2D BusinessClass array called myOwnedBusinesses. I create it the following way:
var myOwnedBusinesses: [[BusinessClass]] = [[], []]
In my application, I have a tableView which contains all elements of allBusinesses, where each section contains the rows of the second dimension of the array, so that: allBusinesses[section][row]. When I select a random cell in the tableView, the corresponding BusinessClass element is added to the myOwnedBusinesses array, in the following way:
myOwnedBusinesses[selectedSection].append(allBusinesses[selectedSection][selectedRow])
As you can imagine from seeing the code, if I for instance select the cell at section 0 row 3, then select the cell at section 0 row 2, the order of myOwnedBusinesses will be wrong, being the opposite of what allBusinesses is. As a conclusion, I want to maintain the same order between the two arrays, even though the myOwnedBusinesses array is not always filled.

Here is my solution
let section0 = ["a", "b", "c", "d", "e", "f"]
let section1 = ["h", "i", "j", "k", "l", "m"]
section0.index(of: "b")
var all:[[String]] = [[],[]]
all[0].append(contentsOf: section0)
all[1].append(contentsOf: section1)
To keep the original indexes flatten the original array
let all_flat = all.flatMap {$0}
Let's say the user selects the cells in this order : "d", "e", "a", "h", "m", and "k".
var myArray = [["d", "e", "a"], ["h", "m", "k"]]
And then sort each array inside myArray
myArray = myArray.map {
$0.sorted { str1, str2 in
return all_flat.index(of: str1)! < all_flat.index(of: str2)!
}
}
For your case :
let allBusinesses_flat = allBusinesses.flatMap {$0}
myOwnedBusinesses = myOwnedBusinesses.map {
$0.sorted { b1, b2 in
return all_flat.index(of: b1)! < all_flat.index(of: b2)!
}
}
This solution is expensive, memory wise. Storing the selected indexes would be preferable.

Related

How do I 'deal' the contents of an array to 'n' other arrays using Swift?

I have a source array with an unknown number of elements:
let sourceArray = ["A", "B", "C", "D", "E", "F", "G", "H", "I"]
I need to 'deal' those elements out into a given number of new arrays.
For example, given '3' empty arrays, the result would look like this:
let result = [["A", "D", "G"],["B", "E", "H"],["C", "F", "I"]]
This is what I've come up with, which technically works but feels pretty clunky. Any suggestions for improvement?
func createArrays(sourceArray: [String], count: Int) -> [[String]] {
var resultArrays = [[String]]()
for i in 0..<count {
var newArray = [String]()
var currentIndex = i
for (index, item) in sourceArray.enumerated() {
if index == currentIndex {
newArray.append(item)
currentIndex = currentIndex + count
}
}
resultArrays.append(newArray)
}
return resultArrays
}
Personally I would use reduce(into:).
extension Sequence {
func deal(into howMany: Int) -> [[Element]] {
self.enumerated().reduce(into: Array(repeating: [Element](), count: howMany)) {
$0[$1.offset % howMany].append($1.element)
}
}
}
Now you just say
let output = sourceArray.deal(into: 3)
or however many piles you want. It isn't very sophisticated but I like it because it does exactly what we intuitively would think of as dealing: it literally deals out the elements into piles, one at a time, exactly as you physically do when you deal out a deck of cards into hands, one card per pile round in a circle until the deck is exhausted.
Alexander's recommendation is one way to tackle the problem, but striding by the share count, once for each share, yields the same result as chunking and then transposing (except that transposing isn't defined when the chunks are not of equal counts).
["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"].distributedUniformly(shareCount: 4) as Array
[["A", "E", "I"], ["B", "F", "J"], ["C", "G"], ["D", "H"]]
import Algorithms
public extension Sequence {
/// Distribute the elements as uniformly as possible, as if dealing one-by-one into shares.
/// - Note: Later shares will be one smaller if the element count is not a multiple of `shareCount`.
#inlinable func distributedUniformly(shareCount: Int)
-> LazyMapSequence<Range<Int>, StrideSequence<DropFirstSequence<Self>>> {
(0..<shareCount).lazy.map {
dropFirst($0).striding(by: shareCount)
}
}
/// Distribute the elements as uniformly as possible, as if dealing one-by-one into shares.
/// - Note: Later shares will be one smaller if the element count is not a multiple of `shareCount`.
#inlinable func distributedUniformly(shareCount: Int) -> [[Element]] {
.init(distributedUniformly(shareCount: shareCount).map(Array.init))
}
}

How to iterate over consecutive elements

I'm looking for a method that is similar to Array#combination, but the order matters.
Given this input:
array = ['a','b','c','d','e']
I'd like to get:
[['a','b','c'],['b','c','d'],['c','d','e']]
I'm trying to find the method that does this:
array = ['a','b','c','d','e']
x,a = 3,[]
until x > (ary.size) do
a += (0.upto(ary.size - x).map{|i| ary[i..(x-1)+i]} )
x += 1
end
The Enumerable documentation is your friend:
array = ['a','b','c','d','e']
array.each_cons(3).to_a
# => [["a", "b", "c"], ["b", "c", "d"], ["c", "d", "e"]]
each_cons(n) { ... }
Iterates the given block for each array of consecutive elements. If no block is given, returns an enumerator.

Is there a Ruby Array method that returns two sub-arrays: one of specified size and another of the remaining contents?

I need to render the contents of the first 5 elements of an array and display "And X more" on a web page. Is there a built-in method on Array (or Enumerable) that easily separates one array into two sub-arrays: the first consisting of up to a fixed size and the second consisting of the array remainder?
I'm looking for one simple method call that will do this for me. Most of the methods that I looked at (like Enumerable#partition) use a logical condition to divide the array and don't supply the index to the block.
I just wrote the following code to do what I want. Please save me from myself and direct me to a method that already does it.
class Array
def bifurcate(size=length)
if size < 0
raise ArgumentError, "attempt to bifurcate using negative size"
end
remainder_size = length - size
if remainder_size < 0
remainder_size = 0
end
[
first(size),
last(remainder_size)
]
end
end
('a'..'g').to_a.bifurcate(2)
# => [["a", "b"], ["c", "d", "e", "f", "g"]]
('a'..'g').to_a.bifurcate(20)
# => [["a", "b", "c", "d", "e", "f", "g"], []]
('a'..'g').to_a.bifurcate()
# => [["a", "b", "c", "d", "e", "f", "g"], []]
('a'..'g').to_a.bifurcate(0)
# [[], ["a", "b", "c", "d", "e", "f", "g"]]
('a'..'g').to_a.bifurcate(-1)
# ArgumentError: attempt to bifurcate using negative size
Also, let me qualify that I want one simple method call to do what I want. Also consider that the starting array may contain duplicate values and this method needs to respect the original array and return duplicates.
You can use Enumerable#partition along with Enumerator#with_index method, as shown below:
size = 2
(1..6).partition.with_index { |_,i| i < size }
#=> [[1, 2], [3, 4, 5, 6]]
Alternatively, if your input array can be mutated, then, following will also do the trick
[array.shift(size), array]
[array.take(3), array.drop(3)]
# [["a", "b", "c"], ["d", "e", "f", "g"]]
Hope it helps :)
Use Array#[]:
[arr[0,size_of_first], arr[size_of_first..-1] || []]

Ruby on Rails - How to know how many time the same Object appears in Array using Active Record?

How to know how many times the same Object appears in Array?
I want to check how many times I found the object, like:
array = ['A','A','A','B','B','C','C','C','D']
So, A appeared three times, B twice, C three too, and only one for D.
I know that if I use "find_all", like:
array.find_all{ |e| array.count(e) > 1 }
I will get with answer
["A", "A", "A", "B", "B", "C", "C", "C"]
but, how I can count this? I want something like:
A = 3, B = 2, C = 3, D = 1.
You can use inject on the array to iterate over the array and pass a hash into each iteration to store data. So to retrieve the count of the array you gave you would do this:
array = ["A", "A", "A", "B", "B", "C", "C", "C"]
array.inject(Hash.new(0)) do |hash, array_item|
hash[array_item] += 1
hash # this will be passed into the next iteration as the hash parameter
end
=> {"A"=>3, "B"=>2, "C"=>3}
Passing in Hash.new(0) rather than {} will mean that the default value for each key when you first encounter it will be 0.

Determine if the elements in arrays are arranged accordingly

I have arrays basket = ["O", "P", "W", "G"] and sack = ["G", "P", "O", "W"]. How can I compare these arrays to determine if the elements are arranged in the same order on not?
You can use:
basket == sack #=> false, for given values
If you compare them, having the same order:
basket.sort == sack.sort #=> true
Also, please check "Comparing two arrays in Ruby" for a discussion on comparing arrays.
If both arrays can contain different number of elements and possibly some extra elements, and you want to find out whether those elements that are common to both arrays appear in exact same order, then, you could do something like below:
basket = ["D", "O", "P", "W", "G", "C"]
sack = ["Z", "O", "W", "P", "G", "X"]
p (basket - (basket - sack)) == (sack - (sack - basket))
Here is the solution I was able to come up with.
ordered = 0
disordered = 0
index = 0
while index < basket.length
if basket[index] == sack[index]
ordered+= 1
elsif basket.include?(sack[index]) && (basket[index] != sack[index])
disordered+= 1
end
index += 1
end
puts" there are #{ordered} ordered common items and #{disordered} disordered common items"
I hope it helps.

Resources