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
Related
So i want to sort an array of UnitMass, but i want to sort it in a specific layout. I know how to sort it alphabetically, but that's not quite what i want anyone that know how i can do this?
Is it possible to sort an array like this, and how can i do this or could i define a way that the array should look?
let spesificLayoutOfUnits =[ UnitMass.grams, UnitMass.kilograms, UnitMass.metricTons, UnitMass.stones, UnitMass.pounds, UnitMass.ounces ]
var toBeSorted = [ UnitMass.kilograms, UnitMass.pounds, UnitMass.metricTons, UnitMass.ounces, UnitMass.grams ]
I use the spesificLayoutOfUnits array as an array of all of the options the user has. Then add units form this array to the toBeSorted array which is the array of the chosen units.
var options = spesificLayoutOfUnits as! [UnitMass]
var tempArray = [UnitMass]()
while toBeSorted.endIndex > positionOfUnit {
tempArray.append(toBeSorted.removeLast())
}
toBeSorted.append(options.remove(at: positionOfUnit))
while !tempArray.isEmpty {
toBeSorted.append(tempArray.removeLast())
}
I'd suggest building up a dictionary that maps UnitMass to its desired position and then using that for the sort:
let specificLayoutOfUnits = [ UnitMass.grams, UnitMass.kilograms, UnitMass.metricTons, UnitMass.stones, UnitMass.pounds, UnitMass.ounces ]
// Create a dictionary to map UnitMass to Int position
var position = [UnitMass : Int]()
for (idx, um) in specificLayoutOfUnits.enumerated() {
position[um] = idx
}
var toBeSorted = [ UnitMass.kilograms, UnitMass.pounds, UnitMass.metricTons, UnitMass.ounces, UnitMass.grams ]
// Sort the array by position. Use Int.max if the UnitMass has no
// position to sort it to the end of the array
let sorted = toBeSorted.sorted { position[$0, default: Int.max] < position[$1, default: Int.max] }
Explanation:
calling .enumerated() on the specificLayoutOfUnits creates a list of tuples where the first element is the position and the second is the unit: [(0, UnitMass.grams), (1, UnitMass.kilograms), (2, UnitMass.metricTons), ...].
Next, we iterate through this list to build up a dictionary that maps the unit to its position: [UnitMass.grams: 0, UnitMass.kilograms: 1, UnitMass.metricTons: 2, ...]
This dictionary is used to sort the second array by the elements position in the first. .sorted(by:) takes a closure that compares two elements and returns a Bool which says if the first element is ordered before the second. Here, we use the position of the two elements in the first array to decide the order. Note that it is possible to have an element in the second array that doesn't appear in the first. In that case, we use the special version of the dictionary lookup to return Int.max for the position. This will cause all UnitMass values without a position to be sorted to the end of the array.
In Kotlin we can do:
val arr = intArrayOf(1,2,3)
if (2 in arr)
println("in list")
But if I want to check if 2 or 3 are in arr, what is the most idiomatic way to do it other than:
if (2 in arr || 3 in arr)
println("in list")
I'd use any() extension method:
arrayOf(1, 2, 3).any { it == 2 || it == 3 }
This way, you traverse the array only once and you don't create a set instance just to check whether it's empty or not (like in one of other answers to this question).
This is the shortest and most idiomatic way I can think of using any and in:
val values = setOf(2, 3)
val array = intArrayOf(1, 2, 3)
array.any { it in values }
Of course you can use a functional reference for the in operator as well:
array.any(values::contains)
I use setOf for the first collection because order does not matter.
Edit: I switched values and array, because of alex.dorokhow's answer. The order doesn't matter for the check to work but for performance.
The OP wanted the most idiomatic way of solving this. If you are after a more efficient way, go for aga's answer.
You can use intersect method, it takes iterable as paramter and returns set containing only items that are both in your collection and in iterable you provided. Then on that set you just need to do size check.
Here is sample:
val array1 = arrayOf(1, 2, 3, 4, 5, 6)
val array2 = arrayOf(2, 5)
// true if array1 contains any of the items from array2
if(array1.intersect(array2.asIterable()).isNotEmpty()) {
println("in list")
}
Combining #aga and #willi-mentzel solutions for better efficiency and dynamic set of checked values:
val numbers = setOf(2, 3)
arrayOf(1, 2, 3).any(numbers::contains)
Is this case the array will be iterated completely only once (at most, in the worst case).
This is more efficient than (suggested by #WilliMentzel):
numbers.any(arrayOf(1, 2, 3)::contains) // don't do that!
Where the array will be iterated set.count times in the worst case.
Note that Set.contains has O(1) complexity, but IntArray::contains has O(N).
Of course, this optimization makes sense only if the set or array are big enough.
Another handy way is actually not Kotlin's but with use of Java Collections.
It is also good to know it.
Collections.disjoint(Collection<?> c1, Collection<?> c2)
Returns {#code true} if the two specified collections have no elements
in common.
#Test
fun disjointCollections() {
val list = listOf(1, 2, 3)
assertTrue(Collections.disjoint(list, listOf(7, 8)))
assertFalse(Collections.disjoint(list, listOf(1)))
}
For me, the best way to solve this is to define a containsAny(elements:) extension function on the Array and/or Collection class. All of the places where you need to check whether an Array or Collection contains any of the elements of another then remains nice and neat, as follows:
arrayOf(1, 2, 3).containsAny(2, 3)
The implementation detail is tucked away inside of that function and can be changed in a single place in future should you wish to change the implementation.
Here's an example implementation of an extension function defined on the Collection class, the details of which are inspired by other answers in this thread:
/**
* Returns true if the receiving collection contains any of the specified elements.
*
* #param elements the elements to look for in the receiving collection.
* #return true if any element in [elements] is found in the receiving collection.
*/
fun <T> Collection<T>.containsAny(vararg elements: T): Boolean {
return containsAny(elements.toSet())
}
/**
* Returns true if the receiving collection contains any of the elements in the specified collection.
*
* #param elements the elements to look for in the receiving collection.
* #return true if any element in [elements] is found in the receiving collection.
*/
fun <T> Collection<T>.containsAny(elements: Collection<T>): Boolean {
val set = if (elements is Set) elements else elements.toSet()
return any(set::contains)
}
Likewise, here's an extension function defined on the Array class:
/**
* Returns true if the receiving array contains any of the specified elements.
*
* #param elements the elements to look for in the receiving array.
* #return true if any element in [elements] is found in the receiving array.
*/
fun <T> Array<T>.containsAny(vararg elements: T): Boolean {
return any(elements.toSet()::contains)
}
I think it's most readable to write the statement the other way around:
val arr = intArrayOf(1,2,3)
val match = setOf(2, 3).any(arr::contains)
It might be even possible to use ranges in certain scenarios:
val match = (2..3).any(arr::contains)
In the end, your solution looks pretty good to me already. Although not using fancy library functionality.
You can make an extension function to check for more values:
infix fun <T> Iterable<T>.containsAny(values: Iterable<T>): Boolean =
intersect(values).isNotEmpty()
Have a look at this blog post: https://www.codevscolor.com/kotlin-check-array-contains-one-multiple-values
Sample code
fun main() {
val givenArray = intArrayOf(1, 2, 3, 4, 5, 7, 9, 11)
println(givenArray.any{ it == 5 || it == 12})
println(givenArray.any{ it == 5 || it == 11})
println(givenArray.any{ it == 15 || it == 21})
}
If you use Apache Commons in your project, you can use CollectionUtils.containsAny.
we can use any() funtion and a Set
val arr = intArrayOf(1,2,3)
if(arr.any{ it in setOf(2,3) })
println("in list")
I have been porting over an algorithm I've been using in Java (Android) to Swift (iOS), and have run into some issues with speed on the Swift version.
The basic idea is there are objects with depths (comment tree), and I can hide and show replies from the dataset by matching against a list of hidden objects. Below is a visualization
Top
- Reply 1
- - Reply 2
- - Reply 3
- Reply 4
and after hiding from the dataset
Top
- Reply 1
- Reply 4
The relevant methods I've converted from Java are as follows
//Gets the "real" position of the index provided in the "position" variable. The comments array contains all the used data, and the hidden array is an array of strings that represent items in the dataset that should be skipped over.
func getRealPosition(position: Int)-> Int{
let hElements = getHiddenCountUpTo(location: position)
var diff = 0
var i = 0
while i < hElements {
diff += 1
if(comments.count > position + diff && hidden.contains(comments[(position + diff)].getId())){
i -= 1
}
i += 1
}
return position + diff
}
func getHiddenCountUpTo(location: Int) -> Int{
var count = 0
var i = 0
repeat {
if (comments.count > i && hidden.contains(comments[i].getId())) {
count += 1
}
i += 1
} while(i <= location && i < comments.count)
return count
}
This is used with a UITableViewController to display comments as a tree.
In Java, using array.contains was quick enough to not cause any lag, but the Swift version calls the getRealPosition function many times when calling heightForRowAt and when populating the cell, leading to increasing lag as more comment ids are added to the "hidden" array.
Is there any way I can improve on the speed of the array "contains" lookups (possibly with a different type of collection)? I did profiling on the application and "contains" was the method that took up the most time.
Thank you
Both Java and Swift have to go through all elements contained in the array. This gets slower and slower as the array gets larger.
There is no a priori reason for Java to fare better, as they both use the exact same algorithm. However, strings are implemented very differently in each language, so that could make string comparisons more expensive in Swift.
In any case, if string comparison slows you down, then you must avoid it.
Easy fix: use a Set
If you want a simple performance boost, you can replace an array of strings with a set of strings. A set in Swift is implemented with a hash table, meaning that you have expected constant time query. In practice, this means that for large sets, you will see better performance.
var hiddenset Set<String> = {}
for item in hidden {
strset.insert(item)
}
For best performance: use a BitSet
But you should be able to do a whole lot better than even a set can do. Let us look at your code
hidden.contains(comments[i].getId()))
If you are always accessing hidden in this manner, then it means that what you have is a map from integers (i) to Boolean values (true or false).
Then you should do the following...
import Bitset;
let hidden = Bitset ();
// replace hidden.append(comments[i].getId())) by this:
hidden.add(i)
// replace hidden.contains(comments[i].getId())) by this:
hidden.contains(i)
Then your code will really fly!
To use a fast BitSet implementation in Swift, include the following in Package.swift (it is free software):
import PackageDescription
let package = Package(
name: "fun",
dependencies: [
.Package(url: "https://github.com/lemire/SwiftBitset.git", majorVersion: 0)
]
)
i think you need the realPosition to link from a tap on a row in the tableview to the source array?
1) make a second array with data only for the tableViewDataSource
copy all visible elements to this new array. create a special ViewModel as class or better struct which only has the nessesary data to display in the tableview. save in this new ViewModel the realdataposition also as value. now you have a backlink to the source array
2) then populate this TableView only from the new datasource
3) look more into the functional programming in swift - there you can nicer go over arrays for example:
var array1 = ["a", "b", "c", "d", "e"]
let array2 = ["a", "c", "d"]
array1 = array1.filter { !array2.contains($0) }
or in your case:
let newArray = comments.filter{ !hidden.contains($0.getId()) }
or enumerated to create the viewmodel
struct CommentViewModel {
var id: Int
var text: String
var realPosition: Int
}
let visibleComments: [CommentViewModel] = comments
.enumerated()
.map { (index, element) in
return CommentViewModel(id: element.getId(), text: element.getText(), realPosition: index)
}
.filter{ !hidden.contains($0.id) }
I have two arrays
main_array = [[0,[1,4,5]], [1,[4,6,8]], [2,[5,6,7,8]], [3,[9,8]], [4,[7,2]]]
other_array = [[1,4,5],[9,8]]
What I want to do is to delete those elements in main_array if it can be found in other_array. (Meaning I will be left with [[1,[4,6,8]], [2,[5,6,7,8]],[4,[7,2]]] ) So I did the following:
other_array.each { |x|
for i in 0..main_array.length-1
main_array.delete(x)
}
It didn't work. Any clues on how I should approach this?
main_array.reject { |_,a| other_array.include?(a) }
#=> [[1,[4,6,8]], [2,[5,6,7,8]], [4,[7,2]]]
You can use Enumerable#reject (this is a module that is included in almost all collections in Ruby, such as Arrays, Hashes, Sets and etc.), it returns new array with removed elements, based on some condition:
main_array.reject { |item| other_array.include? item[1] }
Here item is every item in main_array. You can also "unwrap" your item if you want to control its elements individually:
main_array.reject { |(key, val)| other_array.include? val }
I'd recommend you to go and check the link above, there are also lots of interesting things over there.
I know that true mutability can not be achieved in swift. I have an array interspersed with different types of contents.
let myArray = String[]();
var array = ["First","Second","Third",1,0.4,myArray,"dsaa"]
I learned from the above post I have linked that we will be able to append items to an array. But each time I add a new item to the array I have declared above I get this error:
could not find an overload for '+=' that accepts the supplied
arguments
But when the array is homogeneous, I am able to add an item which is same as the already present items, without hassle. but still the item with a different type can not be added.
If you declare your second array explicitly as AnyObject[], you can do it:
let myArray = String[]()
var array:AnyObject[] = ["First", "Second", "Third", 1, 0.4, myArray, "dsaa"]
let n1 = array.count // 7
array += "next"
let n2 = array.count // 8