Given some arrays in Kotlin
let a = arrayOf("first", "second")
val b = arrayOf("first", "second")
val c = arrayOf("1st", "2nd")
Are there built-in functions to the Kotlin std-lib that tests two arrays for (value) equality for each element?
Thus resulting in:
a.equals(b) // true
a.equals(c) // false
equals() is actually returning false in both cases, but maybe there are built-in functions to Kotlin that one could use?
There is the static function java.utils.Arrays.deepEquals(a.toTypedArray(), b.toTypedArray()) but I would rather prefer an instance method as it would work better with optionals.
In Kotlin 1.1 you can use contentEquals and contentDeepEquals to compare two arrays for structural equality. e.g.:
a contentEquals b // true
b contentEquals c // false
In Kotlin 1.0 there are no "built-in functions to the Kotlin std-lib that tests two arrays for (value) equality for each element."
"Arrays are always compared using equals(), as all other objects" (Feedback Request: Limitations on Data Classes | Kotlin Blog).
So a.equals(b) will only return true if a and b reference the same array.
You can, however, create your own "optionals"-friendly methods using extension functions. e.g.:
fun Array<*>.equalsArray(other: Array<*>) = Arrays.equals(this, other)
fun Array<*>.deepEqualsArray(other: Array<*>) = Arrays.deepEquals(this, other)
P.S. The comments on Feedback Request: Limitations on Data Classes | Kotlin Blog are worth a read as well, specifically comment 39364.
Kotlin 1.1 introduced extensions for comparing arrays by content:
contentEquals and contentDeepEquals.
These extensions are infix, so you can use them the following way:
val areEqual = arr1 contentEquals arr2
And if you want to compare contents of two Collections ignoring the order you can add this simple extension:
infix fun <T> Collection<T>.sameContentWith(collection: Collection<T>?)
= collection?.let { this.size == it.size && this.containsAll(it) }
...and use it like this:
a = mutableListOf<String>()
b = mutableListOf<String>()
isListsHasSameContent = a sameContentWith b
For a simple equals (not deep equals!):
otherArray.size == array.size && otherArray.filter { !array.contains(it) }.isEmpty()
This code will compare the size and the items. The items are compared with .equals().
In koltin if your array or ArrayList is type of a data Class you can simply compare array :a and array :b
like this
if(a == b)
it will return simple boolean if it matched all the value of both arrays, but if you are matching other-than data data Class then you can use this extension to compare it with single value
fun <T> Array<T>.isEqual(comparable: Array<T>): Boolean {
var isChanged = true
if (this.size == comparable.size) {
for (index in 0 until comparable.size) {
if (this[index] != comparable[index]) {
isChanged = false
break
}
}
} else {
isChanged = false
}
return isChanged
}
then you can use your array
val a = arrayOf("first", "second")
val b = arrayOf("first", "second")
println(a.isEqual(b)) //true
a[0] = "third"
println(a.isEqual(b)) //false
Related
I am given a list of strings and I need to see if they are in alphabetical order.
I know I need to use a for loop and check the first character of each string but I don't know how to progress from there.
for (item in array)
println(item[0])
For example ["adam", "ben", "chloe"] should return true.
And likewise for ["ben", "adam", "chloe"] should return false.
UPD: Since Kotlin 1.2 the following is available:
Most efficient variant, creates the smallest amount of intermediate objects:
listOf(1, 2, 3).asSequence().zipWithNext { a, b -> a <= b }.all { it }
Note on efficiency: Creates only two Sequence objects and one function object. Uses Boolean constants as an intermediate value. The final all invocation is inlined as a while loop over the sequence.
Slightly less efficient variant:
listOf(1, 2, 3).asSequence().windowed(2).all { (a, b) -> a <= b }
It's slightly less efficient, as it creates creates intermediate List(a, b) for every element of the original list.
Both of these variants were listed in the answers below by #Billbucket and #AshishChaudhary.
Old answer, for previous versions of Kotlin:
Here is a one liner:
val a = listOf("a", "b", "c")
a.zip(a.drop(1)).all { (a, b) -> a <= b }
// true
Explanation:
a.zip(a.drop(1)) returns pairs of neighbour elements. If in every pair first element is less or equal to the next, array is in order.
If you want to improve performance, you can wrap your array in lazy sequence first. In this case array won't be copied:
a.asSequence().let { it.zip(it.drop(1)).all { (a, b) -> a < b } }
The whole thing is O(N) (single pass through array), which is optimal for this task.
you can use a simple one-liner:
array.zipWithNext { s1, s2 -> s1 <= s2 }.all { it }
Im sure you could do your desired task completely using a for-loop.
However in Kotlin I personally think it would be more idiomatic to do something like this using until:
fun isAlphabetical(stringsArray: Array<String>): Boolean {
if (stringsArray.size < 2) return true
return (1 until stringsArray.size).none { stringsArray[it] <= stringsArray[it - 1] }
}
fun main(args: Array<String>) {
val testCases = arrayOf(arrayOf("adam", "ben", "chloe"), arrayOf("ben", "adam", "chloe"))
for(testCase : Array<String> in testCases){
println("The strings are: ${testCase.joinToString()}")
if (isAlphabetical(testCase)) {
println("Strings are in alphabetical order.\n")
} else {
println("Strings are not in alphabetical order.\n")
}
}
}
Output:
The strings are: adam, ben, chloe
Strings are in alphabetical order.
The strings are: ben, adam, chloe
Strings are not in alphabetical order.
Basically you only compare each element of the array with the previous element (using <=) if the length of the array is more than 1.
A very simple and easy way to accomplish it is by sorting and comparing the original list with the sorted one (two lists are equal when they have the exact same elements in the same order). Since you mentioned you are dealing with an array, you first need to convert it to a list. This solution is not the best in terms of performance (it's O(n log n) and converts the array twice to a list) but it's very readable:
val test = array.asList() == array.asList().sorted()
The full code for your question could be:
println(if (array.asList() == array.asList().sorted()) "Strings are in alphabetical order." else "Strings are not in alphabetical order.")
Another solution:
val list = listOf("a", "b", "c")
list.windowed(2).none { (a, b) -> a > b }
// true
In case you want to compare arbitrary Comparable list:
fun <T : Comparable<T>> isCollectionSortedAsc(list: Collection<T>): Boolean {
return list.zipWithNext { a, b -> a.compareTo(b) == -1 }.all { it }
}
Based on the accepted answer above: https://stackoverflow.com/a/47296632/4919972
Yet another solution :)
data class Result(val isInOrder: Boolean, val lastString: String) {
val toString = when {
isInOrder -> "Strings are in alphabetical order."
else -> "Strings are not in alphabetical order."
}
}
fun Array<String>.isInAlphabeticalOrder() =
this.fold(Result(true, ""), { acc, word -> Result(acc.isInOrder && acc.lastString < word, word) })
fun main(args: Array<String>) {
val test1 = arrayOf("adam", "ben", "chloe")
val test2 = arrayOf("ben", "adam", "chloe")
println(test1.isInAlphabeticalOrder().toString)
println(test2.isInAlphabeticalOrder().toString)
}
I'm looking for a way to sort a Swift array based on a Boolean value.
I've got it working using a cast to NSArray:
var boolSort = NSSortDescriptor(key: "selected", ascending: false)
var array = NSArray(array: results)
return array.sortedArrayUsingDescriptors([boolSort]) as! [VDLProfile]
But I'm looking for the Swift variant, any ideas?
Update
Thanks to Arkku, I've managed to fix this using the following code:
return results.sorted({ (leftProfile, rightProfile) -> Bool in
return leftProfile.selected == true && rightProfile.selected != true
})
Swift's arrays can be sorted in place with sort or to a new array with sorted. The single parameter of either function is a closure taking two elements and returning true if and only if the first is ordered before the second. The shortest way to use the closure's parameters is by referring to them as $0 and $1.
For example (to sort the true booleans first):
// In-place:
array.sort { $0.selected && !$1.selected }
// To a new array:
array.sorted { $0.selected && !$1.selected }
(edit: Updated for Swift 3, 4 and 5, previously sort was sortInPlace and sorted was sort.)
New (for Swift 1.2)
return results.sort { $0.selected && !$1.selected }
Old (for Swift 1.0)
Assuming results is of type [VDLProfile] and VDLProfile has a Bool member selected:
return results.sorted { $0.selected < $1.selected }
See documentation for sorted
Swift’s standard library provides a function called sorted, which sorts an array of values of a known type, based on the output of a sorting closure that you provide
reversed = sorted(array) { $0 > $1 }
reversed will be a new array which will be sorted according to the condition given in the closure.
Trying to understand how swift compares arrays.
var myArray1 : [String] = ["1","2","3","4","5"]
var myArray2 : [String] = ["1","2","3","4","5"]
// 1) Comparing 2 simple arrays
if(myArray1 == myArray2) {
println("Equality")
} else {
println("Equality no")
}
// -> prints equality -> thanks god
// 2) comparing to a "copy" of an array
// swift copies arrays when passed as parameters (as per doc)
func arrayTest(anArray: [String]) -> Bool {
return anArray == myArray1
}
println("Array test 1 is \(arrayTest(myArray1))")
println("Array test 2 is \(arrayTest(myArray2))")
// equality works for both
myArray2.append("test")
println("Array test 2 is \(arrayTest(myArray2))")
// false (obviously)
myArray2.removeAtIndex(5)
println("Array test 2 is \(arrayTest(myArray2))")
// true
Apple says there are optimisations behind the scene on array copies. Looks like sometimes - not always - structures are actually copied or not.
That said,
1) is == iterating over all the array to perform a element-based comparison ? (looks like it)
-> How about performance / memory usage on very large arrays then ?
2) Are we sure == will ever return true if all elements are equal ? I have bad memories of == on Java Strings
3) Is there a way to check if myArray1 and myArray2 are technically using the same "memory location" / pointer / etc. ? i'm after understanding how the optimisation works and potential caveats.
Thanks.
You’re right to be slightly nervous about ==:
struct NeverEqual: Equatable { }
func ==(lhs: NeverEqual, rhs: NeverEqual)->Bool { return false }
let x = [NeverEqual()]
var y = x
x == y // this returns true
[NeverEqual()] == [NeverEqual()] // false
x == [NeverEqual()] // false
let z = [NeverEqual()]
x == z // false
x == y // true
y[0] = NeverEqual()
x == y // now false
Why? Swift arrays do not conform to Equatable, but they do have an == operator, defined in the standard library as:
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool
This operator loops over the elements in lhs and rhs, comparing the values at each position. It does not do a bitwise compare – it calls the == operator on each pair of elements. That means if you write a custom == for your element, it’ll get called.
But it contains an optimization – if the underlying buffers for the two arrays are the same, it doesn’t bother, it just returns true (they contain identical elements, of course they’re equal!).
This issue is entirely the fault of the NeverEqual equality operator. Equality should be transitive, symmetric and reflexive, and this one isn't reflexive (x == x is false). But it could still catch you unawares.
Swift arrays are copy-on-write – so when you write var x = y it doesn’t actually make a copy of the array, it just points x’s storage buffer pointer at y’s. Only if x or y are mutated later does it then make a copy of the buffer, so that the unchanged variable is unaffected. This is critical for arrays to behave like value types but still be performant.
In early versions of Swift, you actually could call === on arrays (also in early versions, the mutating behaviour was a bit different, if you mutated x, y would also change even though it had been declared with let – which freaked people out so they changed it).
You can kinda reproduce the old behaviour of === on arrays with this (very implementation-dependent not to be relied-on except for poking and prodding investigations) trick:
let a = [1,2,3]
var b = a
a.withUnsafeBufferPointer { outer in
b.withUnsafeBufferPointer { inner in
println(inner.baseAddress == outer.baseAddress)
}
}
== in Swift is the same as Java's equals(), it compares values.
=== in Swift is the same as Java's ==, it compares references.
In Swift you can compare array content values as easy as this:
["1", "2"] == ["1", "2"]
But this will not work if you want to compare references:
var myArray1 = [NSString(string: "1")]
var myArray2 = [NSString(string: "1")]
myArray1[0] === myArray2[0] // false
myArray1[0] == myArray2[0] // true
So the answers:
I think the performance is optimal for doing value (not reference)
comparisons
Yes, if you want to compare values
Swift arrays are value type and not reference type. So the memory
location is the same only if you compare it to itself (or use unsafe
pointers)
It depends on how do you want to compare. For example:
["1", "2"] == ["1", "2"] // true
but
["1", "2"] == ["2", "1"] // false
If you need that second case to also be true and are ok with ignoring repetitive values, you can do:
Set(["1", "2"]) == Set(["2", "1"]) // true
(use NSSet for Swift 2)
For compare arrays of custom objects we can use elementsEqual.
class Person {
let ID: Int!
let name: String!
init(ID: Int, name: String) {
self.ID = ID
self.name = name
}
}
let oldFolks = [Person(ID: 1, name: "Ann"), Person(ID: 2, name: "Tony")]
let newFolks = [Person(ID: 2, name: "Tony"), Person(ID: 4, name: "Alice")]
if oldFolks.elementsEqual(newFolks, by: { $0.ID == $1.ID }) {
print("Same people in same order")
} else {
print("Nope")
}
Arrays conform to Equatable in Swift 4.1, negating the caveats mentioned in previous answers. This is available in Xcode 9.3.
https://swift.org/blog/conditional-conformance/
But just because they implemented == did not mean Array or Optional conformed to Equatable. Since these types can store non-equatable types, we needed to be able to express that they are equatable only when storing an equatable type.
This meant these == operators had a big limitation: they couldn’t be used two levels deep.
With conditional conformance, we can now fix this. It allows us to write that these types conform to Equatable—using the already-defined == operator—if the types they are based on are equatable.
If you have an array of custom objects, one has to be careful with the equality test, at least with Swift 4.1:
If the custom object is not a subclass of NSObject, the comparison uses the static func == (lhs: Nsobject, rhs: Nsobject) -> Bool, which has to be defined.
If it is a subclass of NSObject, it uses the func isEqual(_ object: Any?) -> Bool, which has to be overridden.
Please check the following code, and set breakpoints at all return statements.
class Object: Equatable {
static func == (lhs: Object, rhs: Object) -> Bool {
return true
}
}
The following class inheritates Equatable from NSObject
class Nsobject: NSObject {
static func == (lhs: Nsobject, rhs: Nsobject) -> Bool {
return true
}
override func isEqual(_ object: Any?) -> Bool {
return true
}
}
They can be compared with:
let nsObject1 = Nsobject()
let nsObject2 = Nsobject()
let nsObjectArray1 = [nsObject1]
let nsObjectArray2 = [nsObject2]
let _ = nsObjectArray1 == nsObjectArray2
let object1 = Object()
let object2 = Object()
let objectArray1 = [object1]
let objectArray2 = [object2]
let _ = objectArray1 == objectArray2
Whereas comparing arrays in swift, must be clear what are the comparison entities.
Ex:
A = [1,2,3]
B = [2,1,3]
return A == B -> false
The comparison is being made traversing both arrays and comparing its index's data.
return Set(A) == Set(B) -> true
Because of function Set(), the comparison is made by whole single value collection.
"Set operations are not limited to use with other sets. Instead, you
can perform set operations with another set, an array, or any other
sequence type."
Use the “equal to” operator (==) to test whether two sets contain > the same elements.
https://developer.apple.com/documentation/swift/set
Can find more here:
https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html
How do I get the index of a value from a Kotlin array?
My best solution right now is using:
val max = nums.max()
val maxIdx = nums.indices.find({ (i) -> nums[i] == max }) ?: -1
is there a better way?
If you want to get the index of the maximum element you can use 'maxBy' function:
val maxIdx = nums.indices.maxBy { nums[it] } ?: -1
It is more efficient since it will traverse the array only once.
With current Kotlin (1.0) you can use indexOf() extension function on arrays:
val x = arrayOf("happy","dancer","jumper").indexOf("dancer")
All extension functions for arrays are found in the api reference.
In your example:
val maxIdx = nums.indexOf(nums.max())
If you want to find the item based on some predicate, then you can use indexOfFirst and indexOfLast extension functions.
val strings = arrayOf("hello","world","hello")
val firstHelloIndex = strings.indexOfFirst { it == "hello" }
val lastHelloIndex = strings.indexOfLast { it == "hello" }
val a: Array[Int] = Array(1,2,4,5)
val b: Array[Int] = Array(1,2,4,5)
a==b // false
Is there a pattern-matching way to see if two arrays (or sequences) are equivalent?
From Programming Scala:
Array(1,2,4,5).sameElements(Array(1,2,4,5))
You need to change your last line to
a.deep == b.deep
to do a deep comparison of the arrays.
a.corresponds(b){_ == _}
Scaladoc: true if both sequences have
the same length and p(x, y) is true
for all corresponding elements x of
this wrapped array and y of that,
otherwise false
For best performance you should use:
java.util.Arrays.equals(a, b)
This is very fast and does not require extra object allocation. Array[T] in scala is the same as Object[] in java. Same story for primitive values like Int which is java int.
As of Scala 2.13, the deep equality approach doesn't work and errors out:
val a: Array[Int] = Array(1,2,4,5)
val b: Array[Int] = Array(1,2,4,5)
a.deep == b.deep // error: value deep is not a member of Array[Int]
sameElements still works in Scala 2.13:
a sameElements b // true
It didn't look like most of the provided examples work with multidimensional arrays. For example
val expected = Array(Array(3,-1,0,1),Array(2,2,1,-1),Array(1,-1,2,-1),Array(0,-1,3,4))
val other = Array(Array(3,-1,0,1),Array(2,2,1,-1),Array(1,-1,2,-1),Array(0,-1,3,4))
assert(other.sameElements(expected))
returns false, throws an assertion failure
deep doesn't seem to be a function defined on Array.
For convenience I imported scalatest matchers and it worked.
import org.scalatest.matchers.should.Matchers._
other should equal(expected)