I have an array of rectangles and I want to merge all overlapping and adjacent rectangles into each other. The final array should have no overlapping or adjacent rectangles. I tried doing the below code but some rectangles are still left over.
func combine_boxes(boxes: Array) -> void:
var merged := true
while merged:
merged = combine_all_overlapping_box(boxes)
func combine_all_overlapping_box(boxes: Array) -> bool:
var box: Rect2 = boxes[0]
# Remove our box
boxes.remove(0)
# Start from the back of the the array so removals are easier and don't invalidate the index
var merged := false
for j in range(boxes.size()-1, -1, -1):
var collider: Rect2 = boxes[j]
if overlap_adjacent(box, collider):
box = box.merge(collider)
# Remove collider box
boxes.remove(j)
merged = true
# Either add the merge box in, or the unaltered box we remove at the start
boxes.append(box)
return merged
This what I have been able to come up with:
func combine_rects(rects: Array) -> Array:
var result := rects.duplicate()
var merged := true
while merged:
merged = false
rects = result
result = []
while rects.size() > 0:
var current_rect:Rect2 = rects.pop_back()
for index in for index in range(rects.size() - 1, -1, -1):
var other_rect:Rect2 = rects[index]
if current_rect.intersects(other_rect, true):
merged = true
current_rect = current_rect.merge(other_rect)
rects.remove(index)
result.append(current_rect)
return result
Let us look at the inner loop first:
var current_rect:Rect2 = rects.pop_back()
for index in for index in range(rects.size() - 1, -1, -1):
var other_rect:Rect2 = rects[index]
if current_rect.intersects(other_rect, true):
merged = true
current_rect = current_rect.merge(other_rect)
rects.remove(index)
result.append(current_rect)
As you can see I remove an element from rects and store it in current_rect. Then iterate over the rest of the elements, comparing it with them. I use intersects with true as second argument so it also considers adjacent rects as intersecting… If they intersect, I merge them. And also remove the other element with rects.remove(index)… I iterate backwards so I don't have to adjust the index.
Since I set the current_rect with the result of the merge (current_rect = current_rect.merge(other_rect)), I can continue merging more rects into it in the same loop.
Finally I add whatever I ended up with to the result.
If we look at the mid loop, we see that I do this until I exhaust rects:
while rects.size() > 0:
var current_rect:Rect2 = rects.pop_back()
for index in for index in range(rects.size() - 1, -1, -1):
var other_rect:Rect2 = rects[index]
if current_rect.intersects(other_rect):
merged = true
current_rect = current_rect.merge(other_rect)
rects.remove(index)
result.append(current_rect)
Since I only remove from rects (either by pop_back or by remove) it must eventually be empty and the loop ends.
Now look at the broader picture:
func combine_rects(rects: Array) -> Array:
var result := rects.duplicate()
var merged := true
while merged:
merged = false
rects = result
result = []
while rects.size() > 0:
var current_rect:Rect2 = rects.pop_back()
for index in for index in range(rects.size() - 1, -1, -1):
var other_rect:Rect2 = rects[index]
if current_rect.intersects(other_rect):
merged = true
current_rect = current_rect.merge(other_rect)
rects.remove(index)
result.append(current_rect)
return result
We will continue looping as long as we merged something. The merged variable keeps track of that. I set it to true so the flow of execution enters the loop (the alternative would have been to make an infinite loop while true and use break).
For the loop we need to make sure merged starts false, and result is empty. So we do that. But also, we want to iterate over the results of the past iteration. Which is why I set rects = result. And that means that I need the starting rects in result. I took the opportunity to work on a copy (var result := rects.duplicate()).
You should be able to use it like this:
boxes = combine_rects(boxes)
Related
I'm trying to set a dynamic range which changes while iterating inside a for loop. Aka: Jumping from index to index.
For example:
func func(c: [Int]) -> Int {
var currentIndex = 0
for i in currentIndex..<c.count
currentIndex += 3 //After changing the currentIndex value, will my "i" start from currentIndex?
}
So i starts with 0, then it will be 3, then 6 and so on...
When I run this code, "i" sums up as usual like i = 0, i = 1, i = 2... Can I mutate the iteration range?
As in comments, one powerful solution is using build in stride. Another solution is
while currentIndex < c.count
{
//your loop logic
currentIndex += 3
}
you don't need an 'i'.
I want to count how many times numbers from arr1 appear in arr2. I tried intersects with sets however I do not wish to remove duplicates.
var arr1 = [1,4,5,7]
func compareCount(arr2[Int])-> Int {
//arr2 = 1,1,4,5,6,6,3,9,7,7,7,1,7
return count
//returns 9 as there are 9 elements within arr2 that exist within arr1
}
You can use NSCountedSet for that:
var arr1 = [1,4,5,7]
var arr2 = [1,1,4,5,6,6,3,9,7,7,7,1,7]
let countedSet = NSCountedSet(array: arr2)
Then, iterate through arr1 and for each, you'll get easily the number of occurences with count(for:), and with reduce, you can add them:
let numberOfOccurences = arr1.reduce(into: 0) { (result, current) in
let numberOfOccurencesForCurrent = countedSet.count(for: current)
result += numberOfOccurencesForCurrent
}
print("numberOfOccurences: \(numberOfOccurences)")
Edit:
If you don't want to use reduce() (because you want to avoid using it without understanding it), but rather do a more simple loop:
var numberOfOccurences = 0
arr1.forEach({ numberOfOccurences += countedSet.count(for: $0) })
Loop the first array and count every filtered elements in the second one
var count = 0
arr1.forEach( { value in
count += arr2.filter( {$0 == value} ).count
})
var arrayone: ArrayList<String> = arrayListOf("a","b","c")
val arraytwo:ArrayList<String> = arrayListOf(arrayone.removeAt(0))
for (item in arraytwo) {
println(item)
}
I just want to remove item from the first array and make a new array. But this just prints one item at index 0
removeAt(0) removes the first element from the first list and returns it. arrayListOf then uses that removed item to create a new (the second) list.
arrayone then contains: b and c. arraytwo then contains a.
You may want to use drop instead, if you didn't want to touch the first list and if you only wanted to add the remaining items to the new list, e.g.:
var arrayone: ArrayList<String> = arrayListOf("a","b","c")
val arraytwo = arrayone.drop(1)
for (item in arraytwo) {
println(item) // now prints all except the first one...
}
// arrayone contains: a, b, c
// arraytwo contains: b, c
Or use dropLast(1) if you want to drop only the last item. Or use dropWhile/dropLastWhile if you have a condition to apply and drop all until/upto that condition...
If you really want to remove items from the first and add only the removed ones to the second list, then your current approach is ok. If you however wanted to remove items at specific index and have a new list instead just containing the not-removed ones, you need to construct a copy of the first list first and then apply your removeAt on that list, e.g.:
val arraytwo = arrayone.toMutableList().apply {
removeAt(0)
}
// or without the apply:
arraytwo.removeAt(0)
Or you may use filterIndexed to solve that:
val arraytwo = arrayone.filterIndexed { index, _ ->
index != 1 // you can also specify more interesting filters here...
} // filter, map, etc. all return you a new list. If that list must be mutable again, just add a .toMutableList() at the end
By using filterIndexed, filter, drop, etc. you ensure that the first list is kept untouched. If you didn't want to touch the first list in the first place, you may rather want to use listOf or toList, i.e. just a List as type instead, which does not expose mutable functions (check also Kotlin reference regarding Collections: List, Set, Map).
Maybe you are also interested in filter/filterNot and then soon in minus or similar functions to remove unwanted items without index.
removeAt returns the removed element:
abstract fun removeAt(index: Int): E (source)
Removes an element at the specified index from the list.
Return the element that has been removed.
kotlin.stdlib / kotlin.collections.MutableList.removeAt
You're making a new list with one element, the element you removed.
Try:
val arraytwo = ArrayList(arrayone) // Copy the list
arraytwo.removeAt(0)
You never clarified if you want to modify the original list. If you do, just do arrayone.removeAt(0). That's it.
You can also make use of apply:
val arraytwo = ArrayList(arrayone).apply { removeAt(0) }
If you only need to remove items at the start or end, you can use drop (to remove items at the start) or dropLast, but as far as I know there is no collection extension to drop an item in the middle of an iterable (and judging by your comment, you seem to need this.) This makes sense, since an iterable has no concept of size or index.
If you just want to filter one certain element of your array you can use .filterTo(destination, predicate).
For your example it can look like this:
val arrayone = arrayListOf<String>("a", "b", "c")
val arraytwo = arrayListOf<String>()
arrayone.filterTo(arraytwo, { it != "a" })
println(arrayone) //[a, b, c]
println(arraytwo) //[b, c]
For Normal ArrayList:
val arrList = arrayListOf("account", "bec", "csw", "account")
arrList.removeAll { it == "account" }
For Custom ArrayList:
arrList.removeAll { it.key == "account" }
try this ,
var arrayone: ArrayList<String> = arrayListOf("a","b","c")
arrayone.removeAt(0)
val arraytwo:ArrayList<String> = arrayListOf(arrayone)
for (item in arraytwo) {
println(item)
}
val x = arrayListOf(1, 2, 3, 5)
x.removeFirst()
println(x)
val y = x.filterIndexed { index, value -> index != 0 }
println(y)
Output:
[2, 3, 5]
[3, 5]
GL
Source
I am currently trying to make an app for iOS but I can't get some simple code down. Basically I need to randomly select 5 elements from an array list without repeating an element. I have a rough draft, but it only displays one element.
Here is my code:
let array1 = ["salmon", "turkey", "salad", "curry", "sushi", "pizza"]
let randomIndex1 = Int(arc4random_uniform(UInt32(array1.count)))
print(array1[randomIndex1])
You can do it like this:
let array1 = ["salmon", "turkey", "salad", "curry", "sushi", "pizza", "curry", "sushi", "pizza"]
var resultSet = Set<String>()
while resultSet.count < 5 {
let randomIndex = Int(arc4random_uniform(UInt32(array1.count)))
resultSet.insert(array1[randomIndex])
}
let resultArray = Array(resultSet)
print(resultArray)
A set can contain unique elements only, so it can't have the same element more than once.
I created an empty set, then as long as the array contains less than 5 elements (the number you chose), I iterated and added a random element to the set.
In the last step, we need to convert the set to an array to get the array that you want.
SWIFT 5.1
This has been answered, but I dare to come with a more simple solution.
If you take your array and convert it into a Set you will remove any duplicated items and end up with a set of unique items in no particular order since the nature of a Set is unordered.
https://developer.apple.com/documentation/swift/set
If you then convert it back to an array and pick 5 elements you will end up with an array of random items.
But it's just 1 line ;)
Example:
var original = ["A","B","C","C","C","D","E","F","G","H"]
let random = Array(Set(original)).prefix(5)
Example print:
["B", "G", "H", "E", "C"]
The only requirement is your objects must conform to the Hashable protocol. Most standard Swift types do, otherwise, it's often simple to make your own types conform.
https://developer.apple.com/documentation/swift/hashable
If you don't care about changing the original array, the following code will put the picked elements at the end of the array and then it will return the last part of the array as a slice.
This is useful if you don't care about changing the original array, the advantage is that it doesn't use extra memory, and you can call it several times on the same array.
extension Array {
mutating func takeRandomly(numberOfElements n: Int) -> ArraySlice<Element> {
assert(n <= self.count)
for i in stride(from: self.count - 1, to: self.count - n - 1, by: -1) {
let randomIndex = Int(arc4random_uniform(UInt32(i + 1)))
self.swapAt(i, randomIndex)
}
return self.suffix(n)
}
}
Example:
var array = [1,2,3,4]
let a1 = array.takeRandomly(numberOfElements: 2)
let a2 = array.takeRandomly(numberOfElements: 2)
swift-algorithms now includes an extension to Sequence called randomSample.
import Algorithm
var array1 = ["salmon", "turkey", "salad", "curry", "sushi", "pizza"]
array1.randomSample(count: 5)
Just my ¢2:
Moe Abdul-Hameed's solution has one theoretical drawback: if you roll the same randomIndex in every iteration, the while loop will never exit. It's very unlike tho.
Another approach is to create mutable copy of original array and then exclude picked items:
var source = array1
var dest = [String]()
for _ in 1...5 {
let randomIndex = Int(arc4random_uniform(UInt32(source.count)))
dest.append(source[randomIndex])
source.remove(at: randomIndex)
}
print(dest)
var array1 = ["salmon", "turkey", "salad", "curry", "sushi", "pizza"]
while array1.count > 0 {
// random key from array
let arrayKey = Int(arc4random_uniform(UInt32(array1.count)))
// your random number
let randNum = array1[arrayKey]
// make sure the number ins't repeated
array1.removeAtIndex(arrayKey)
}
By removing your picked value from array, prevent's from duplicates to be picked
I have a 100 X 1 (n1) cell array with each cell holding indices of a bigger data set(100 X 100, n2). I made a nested loop in order to access each individual element(index) and compare the values of another data set with these indices with a if condition. If the condition succeeds, I want to delete that element from the original cell array into a new cell array. However when I set the element to [] in matlab, the value of the cell array does not change. The code is below:
for i = 1:length(n1)
for j = 1:length(n1{i, 1})
if n2(i,n1{i,1}(1,j)) > n3(i) && n2(i, n1{i,1}(1,j)) > n4(n1{i, 1}(1, j))
n1{i,1}(1,j) == [];
end
end
end
I take that n1(i,1) is always a row vector so you should use,
n1{i,1}(j) = [];
If n1(i,1) is not a column or row then removing an element from middle would be impossible.
for example:
A = {[1 2 3],[5 8 9]}
A{1,2}(1,2) = []
gives the error: A null assignment can have only one non-colon index.
But A{1,2}(2) = [] is okey.