How to use reduce() on multidimensional arrays in swift - arrays

I want to iterate through a multidimensional array and compare the elements with one another and increment a value if a condition is met
var arrayExample = [[4,8],[15,30],[25,50]];
var noOfSimiRect: Int = 0
arrayExample.reduce(0) {
let a = $0[0] //error: Value of type 'int' has no subscripts
let b = $0[1] //error: Value of type 'int' has no subscripts
let c = $1[0]
let d = $1[1]
if a.isMultiple(of: c) && b.isMultiple(of: d) {
noOfSimiRect += 1
}
}
print(noOfSimiRect)
Alternatively, if I used the following syntax, I am still getting the error there
var arrayExample = [[4,8],[15,30],[25,50]];
var noOfSimiRect: Int = 0
arrayExample.reduce(0) {
let a = $0.0 //error: value of type 'int' has no member '0'
let b = $1.0 //error: value of type 'int' has no member '1'
let c = $0.0 //error: value of type '[int]' has no member 0'
let d = $1.0 //error: value of type '[int]' has no member '1'
if a.isMultiple(of: c) && b.isMultiple(of: d) {
noOfSimiRect += 1
}
}
print(noOfSimiRect)
Thank you to David and Martin for answering my question but I found a catch in both your solutions.
var arrayExample = [[4,8],[15,30],[25,50]];
In the given array I want the following:
compare [4,8] with [15,30] and [25,50]
compare [15,30] with [4,8] and [25,50]
compare [25,50] with [4,8] and [15,30]

It seems you misunderstand the inputs to the closure of reduce. The first input argument is the accumulating result, while the 2nd is the current element of the array. $0 and $1 are not the current and next elements of the array as you assume they are.
You need to iterate over the indices of the array to access 2 subsequent nested arrays and be able to check their elements against each other.
var noOfSimiRect: Int = 0
for index in arrayExample.indices.dropLast() {
let current = arrayExample[index]
let next = arrayExample[index + 1]
if current[0].isMultiple(of: next[0]) && current[1].isMultiple(of: next[1]) {
noOfSimiRect += 1
}
}
print(noOfSimiRect)

Related

Comparing elements at different indices in array Swift

In swift, I want to compare two different indexes in the same array. Right now, My code is something like:
var myArray:[String] = ["1" , "1", "2"]
for i in myArray{
if(myArray[i] == myArray[i + 1]){
// do something
}
}
From this, I get an error:
Cannot convert value of type 'String' to expected argument type 'Int'
How do I go about fixing this?
Not a direct answer to your question but if what you want is to compare adjacent elements in a collection what you need is to zip the collection with the same collection dropping the first element:
let array = ["1" , "1", "2"]
for (lhs,rhs) in zip(array, array.dropFirst()) {
if lhs == rhs {
print("\(lhs) = \(rhs)")
print("do something")
} else {
print("\(lhs) != \(rhs)")
print("do nothing")
}
}
This will print:
1 = 1
do something
1 != 2
do nothing
For-each construction (for i in array) does not provide you with an index, it takes elements from a sequence.
You may want to use ranges like this to aquire indices:
for i in 0 ..< array.count

N-Dimensional array swift

Is there any way to have an n dimensional array in swift? I would like to be able to make a function that creates an array with n dimensions but I cannot figure out how.
Basically something like this:
func ndarray <T> (dimensions: Int...) -> [[T]] { // What do I tell it I return?
var out
for d in dimensions {
out = Array<T>(repeating: out, count: d)
}
return out
}
The above code does not work for obvios reasons but, I think it points out the main problems I am having:
How do I define a return type
How do I actually create the array
Once created how do I traverse and populate the array
Here is the implementation of an N-Dimensional Array. It uses a normal array internally for storage and converts the multi-dimensional indices into a single index for the internal array.
struct NDimArray<T> {
let dimensions: [Int]
var data: [T]
init(dimensions: Int..., initialValue: T) {
self.dimensions = dimensions
data = Array(repeating: initialValue, count: dimensions.reduce(1, *))
}
init(dimensions: Int..., initUsing initializer: () -> T) {
self.dimensions = dimensions
data = (0 ..< dimensions.reduce(1, *)).map { _ in initializer() }
}
// Compute index into data from indices
private func computeIndex(_ indices: [Int]) -> Int {
guard indices.count == dimensions.count else { fatalError("Wrong number of indices: got \(indices.count), expected \(dimensions.count)") }
zip(dimensions, indices).forEach { dim, idx in
guard (0 ..< dim) ~= idx else { fatalError("Index out of range") }
}
var idx = indices
var dims = dimensions
var product = 1
var total = idx.removeLast()
while !idx.isEmpty {
product *= dims.removeLast()
total += (idx.removeLast() * product)
}
return total
}
subscript(_ indices: Int...) -> T {
get {
return data[computeIndex(indices)]
}
set {
data[computeIndex(indices)] = newValue
}
}
}
Example:
// Create a 3 x 4 x 5 array of String with initial value ""
var arr = NDimArray<String>(dimensions: 3, 4, 5, initialValue: "")
for x in 0 ..< 3 {
for y in 0 ..< 4 {
for z in 0 ..< 5 {
// Encode indices in the string
arr[x, y, z] = "(\(x),\(y),\(z))"
}
}
}
// Show internal storage of data
print(arr.data)
["(0,0,0)", "(0,0,1)", "(0,0,2)", "(0,0,3)", "(0,0,4)", "(0,1,0)", "(0,1,1)", "(0,1,2)", "(0,1,3)", "(0,1,4)", "(0,2,0)", "(0,2,1)", "(0,2,2)", "(0,2,3)", "(0,2,4)", "(0,3,0)", "(0,3,1)", "(0,3,2)", "(0,3,3)", "(0,3,4)", "(1,0,0)", "(1,0,1)", "(1,0,2)", "(1,0,3)", "(1,0,4)", "(1,1,0)", "(1,1,1)", "(1,1,2)", "(1,1,3)", "(1,1,4)", "(1,2,0)", "(1,2,1)", "(1,2,2)", "(1,2,3)", "(1,2,4)", "(1,3,0)", "(1,3,1)", "(1,3,2)", "(1,3,3)", "(1,3,4)", "(2,0,0)", "(2,0,1)", "(2,0,2)", "(2,0,3)", "(2,0,4)", "(2,1,0)", "(2,1,1)", "(2,1,2)", "(2,1,3)", "(2,1,4)", "(2,2,0)", "(2,2,1)", "(2,2,2)", "(2,2,3)", "(2,2,4)", "(2,3,0)", "(2,3,1)", "(2,3,2)", "(2,3,3)", "(2,3,4)"]
print(arr[2, 2, 2]) // "(2,2,2)"
print(arr[3, 0, 0]) // Fatal error: Index out of range
print(arr[0, 4, 0]) // Fatal error: Index out of range
print(arr[2]) // Fatal error: Wrong number of indices: got 1, expected 3
Initializing an Array with a Reference Type
As #DuncanC noted in the comments, you have to be careful when initializing an array with a value which is a reference type, because the array will be filled with references to the object and modifying the object at any index will modify all of them.
To solve this, I added a second initializer:
init(dimensions: Int..., initUsing initializer: () -> T)
which takes a closure () -> T which can be used to create a new object for each element of the array.
For example:
class Person {
var name = ""
}
// Pass a closure which creates a `Person` instance to fill the array
// with 25 person objects
let arr = NDimArray(dimensions: 5, 5, initUsing: { Person() })
arr[3, 3].name = "Fred"
arr[2, 2].name = "Wilma"
print(arr[3, 3].name, arr[2, 2].name)
Fred Wilma
Nope, it's not possible. Array dimensions is something that needs to be determined at compile time, while the argument you want to pass to the initializer will not be known until runtime. If you really want to achieve something like this, then you'll need to move the array indexing from compile time to runtime, e.g. by accessing the array via an array of indexes. Still you don't have compile validation, since the array length can at runtime to not match the dimensions of the array.
This problem is similar to the one that attempts to convert a tuple to an array.

Check if array contains element in Swift 4

I am trying to check if array categories contain number 1 as Int since categories = [Int]() for example categories = {1, 2, 3, 4, 5}
I have tried the below code which gives me error Binary operator '==' cannot be applied to operands of type 'Any' and 'Int'
if categories.contains (where: {$0 == 1}) {
// 1 is found
}
also tried it without the where and brackets as below which gives me the same error
if categories.contains { $0 == 1 } {
// 1 is found
}
I tried using just the element as below which gives me error Missing argument label 'where:' in call
if categories.contains(1) {
// 1 is found
}
How can I do that?
It seems like your category array is of type Any
Ways to fix it
You can declare your array as an Int array
var categories: [Int]
OR
You can change the following piece of code
if categories.contains { $0 == 1 } {
// 1 is found
}
to
if categories.contains { ($0 as! Int) == 1 } {
// 1 is found
}
Note: This method might cause your app to crash if your category array has an element other than of type Int
it is working See my output in PlayGround
Code used:
var categories : [Int] = [0,1,2,3,4,5,6,7,8,9]
if categories.contains(5)
{
print("Yes it contains")
}
else
{
print("it do not")
}
and also This condition is working
if categories.contains (where: {$0 == 1}) {
print("yes")
}
see your Array Declaration I think there is main Issue
Declaration 1 :
var categories = [Int]()
categories = [0,1,2,3,4,5,6,7,8,9]
Declaration 2 :
var categories : [Int] = [0,1,2,3,4,5,6,7,8,9]
Regarding error message
Binary operator '==' cannot be applied to operands of type 'Any' and 'Int'
Your array is not an Int array instead it contains Any so it needs typecasting before comparision. Declaration of array is also wrong use [] instead of {}. And typecast object as an int ($0 as! Int) == 1 (I'm using force casting here because I know its an Int array).
There are many ways to check if array contains any element.
1> Just try to get the index of element with guard if index is nil means array doesn't contain the element. Although you didn't declare array in right way still I'm considering it a valid array.
let categories: [Int] = [1, 2, 3, 4, 5]
guard categories.index(of: 1) != nil else {
print("Doesn't Contain")
return
}
print("Contains")
2> Use contains method
if (categories.contains(1)) {
print("Contains")
}
else {
print("Doesn't Contain")
}
3> Not Recommended for this case But still you can get this
let result = categories.filter({$0 == 1})
if result.count == 0 {
print("Doesn't Contain")
}
else {
print("Contains")
}
filter returns an array of element which matches with condition. So that if there are multiple 1 in array so it will give you an array of all elements. And $0 describes the object while enumerating the array.
4> Not Recommended for this case
let contains = categories.contains(where: {$0 == 1})
if contains {
print("Contains")
}
else {
print("Doesn't Contain")
}
Thanks to your comments made me check the declaration of the array and the problem was that was declared as [Any] after I get it's value from the UserDefaults. I have checked and found the solution on How do I save an Int array in Swift using NSUserDefaults?
// old declaration
let categories = userDefaults.array(forKey:"categories") ?? [Int]()
// new correct declaration
var categories = [Int]()
if let temp = userDefaults.array(forKey:"categories") as? [Int] {
categories = temp
}

Doing math with elements of a 2d-array?

So I am making a 2d-array like this:
var kcalVerdier:Array = new Array(92,80,103,36,53);
var alleNumSteppers:Array = new Array(alleNumSteps.numStepMelk.value,alleNumSteps.numStepEgg.value,alleNumSteps.numStepBrød.value,alleNumSteps.numStepSmør.value,alleNumSteps.numStepOst.value);
var c:Array = new Array(kcalVerdier,alleNumSteppers);
function endreAntall(evt:Event)
{
txtTotalKcal.text = String(c[0] * [0]);
}
Is it not possible to do multiply 2 values of a 2-d Array? I get this error:
Scene 1, Layer 'script', Frame 1, Line 17, Column 38 1067: Implicit coercion of a value of type Array to an unrelated type Number.
I don't understand why, c[0][0] should both be integer values or am I misunderstanding?
Which values you actually want to multiply?
By doing:
c[0] * [0]
you're trying to multiply Array by Array.
c[0] would be 1st element of c Array which is in fact kcalVerdier Array.
[0] is making new Array with one element (that is 0);
so it's like
[92,80,103,36,53] * [0]
[EDIT]
Ok, try this piece of code:
// Check if both Arrays are what we want:
trace("kcalVerdier => " + c[0]);
trace("alleNumSteppers => " + c[0]);
trace();
// Gett Arrays lengths
var arr1Length:int = c[0].length;
var arr2Length:int = c[1].length;
// Check if both are the same length
if(arr1Length == arr2Length)
{
// Let's iterate
for(var i:int = 0; i<arr1Length; i++)
{
trace( c[0][i] * c[1][i] );
}
}

Swift Error: Cannot convert value of type 'ArraySlice' to expected argument type

I'm getting this error and am new to Swift. I want to take the last 5 points of an array >= 5, and pass those 5 points as an array argument to a function. How can I achieve this and get past this error?
Cannot convert value of type 'ArraySlice' to expected argument type '[CGPoint]'
if (self.points?.count >= 5) {
let lastFivePoints = self.points![(self.points!.count-5)..<self.points!.count]
let angle = VectorCalculator.angleWithArrayOfPoints(lastFivePoints)
}
You need to convert ArraySlice to Array using method Array(Slice<Type>)
if (self.points?.count >= 5) {
let lastFivePoints = Array(self.points![(self.points!.count-5)..<self.points!.count])
let angle = VectorCalculator.angleWithArrayOfPoints(lastFivePoints)
}
Instead of the range operator you can use prefix(upTo end: Self.Index) method that return ArraySlice which makes your code shorter. Method's definition: The method returns a subsequence from the start of the collection up to, but not including, the specified position (index).
if (self.points?.count >= 5) {
let lastFivePoints = Array<CGPoint>(self.points?.prefix(upTo:5)) as [AnyObject]
let angle = VectorCalculator.angleWithArrayOfPoints(lastFivePoints)
}
// You can also do this
let lastFivePoints = Array<CGPoint>(self.points?[0...4])
I tried using Array(lastFivePoints) but i got error
Type of expression is ambiguous without more context
I ended up doing:
let arr = lastFivePoints.map({ (x) -> T in
return x
})
where T is the content class CGPoint for this example

Resources