For-In Loop and Type Casting Only for Objects Which Match Type

For-in loop and type casting only for objects which match type

You can use a for-loop with a case-pattern:

for case let item as YourType in array {
// `item` has the type `YourType` here
// ...
}

This will execute the loop body only for those items in the
array which are of the type (or can be cast to) YourType.

Example (from
Loop through subview to check for empty UITextField - Swift):

for case let textField as UITextField in self.view.subviews {
if textField.text == "" {
// ...
}
}

Type casting in for-in loop

For Swift 2 and later:

Swift 2 adds case patterns to for loops, which makes it even easier and safer to type cast in a for loop:

for case let button as AClass in view.subviews {
// do something with button
}

Why is this better than what you could do in Swift 1.2 and earlier? Because case patterns allow you to pick your specific type out of the collection. It only matches the type you are looking for, so if your array contains a mixture, you can operate on only a specific type.

For example:

let array: [Any] = [1, 1.2, "Hello", true, [1, 2, 3], "World!"]
for case let str as String in array {
print(str)
}

Output:

Hello
World!

For Swift 1.2:

In this case, you are casting view.subviews and not button, so you need to downcast it to the array of the type you want:

for button in view.subviews as! [AClass] {
// do something with button
}

Note: If the underlying array type is not [AClass], this will crash. That is what the ! on as! is telling you. If you're not sure about the type you can use a conditional cast as? along with optional binding if let:

if let subviews = view.subviews as? [AClass] {
// If we get here, then subviews is of type [AClass]
for button in subviews {
// do something with button
}
}

For Swift 1.1 and earlier:

for button in view.subviews as [AClass] {
// do something with button
}

Note: This also will crash if the subviews aren't all of type AClass. The safe method listed above also works with earlier versions of Swift.

TypeScript type in a for... in loop on an object

As the TypeScript error says, "The left-hand side of a 'for...in' statement cannot use a type annotation."

However, you can create a typed variable within the for...in loop that does have a type, and use that to index your object.

Here's a modified version of the snippet in your question. I've added an empty data variable and removed the querySplit code because those parts don't have the necessary context in your snippet to resolve TypeScript errors.

You'll also note I had to replace your event[a] code with a variable, because accessing the value using square bracket notation repeatedly doesn't work properly with TypeScript's type narrowing.

interface EVENT {
imageURL: string;
artist: string;
location: string;
city: string;
seat: number;
direction: string;
country: string;
type: string;
date: string;
tickets_available: number;
tickets_left: number;
id: string;
description: string;
price: number;
}

// Just creating an empty array so TypeScript doesn't complain about `data`
let data: EVENT[] = [];

data.filter((event: EVENT) => {
// a = key of the object data
for (let a in event) {
// Create new typed variable for this iterations' value of `a`
const key = a as keyof EVENT;
// Look up `event[key]` once so TypeScript can narrow its type
const value = event[key];

let aSplit = typeof value === "string"
? value.split(" ").map((element: string) => element.toLowerCase())
: value;

// More stuff here
}
});

TypeScript Playground

Why scala's pattern maching does not work in for loops for type matching?

This is the long-standing issue 900 and has been discussed many times before. The common workaround is to use something like:

for (y@(_y:String) <- listOfBaseObjects) {
println(y)
}

A nicer version is provided by Jason Zaugg in the comments to the above-mentioned ticket:

object Typed { def unapply[A](a: A) = Some(a) }

for (Typed(y : String) <- listOfBaseObjects) {
println(y)
}

Objective-C Using for-in Loop Making the Loop Enter Only if the Object is of a Certain Type

Just check for the correct class yourself, by using isKindOfClass:

for (MYObject* myObj in simpleArray) {
if (![myObj isKindOfClass:[MYObject class]]) {
// wrong class. continue to next object.
continue;
}
sumNums += myObj.myNum;
}

How to iterate over an array and check element type at the same time in swift?


Solution 1

You can filter your array

let things = [NSDictionary(), NSArray(), NSDictionary(), NSArray()]

let dicts = things.flatMap { $0 as? NSDictionary }

Now dicts is defined as [NSDictionary] an contains only the 2 dictionaries.

Solution 2

You can also perform a for loop only on the values that are dictionaries

let things = [NSDictionary(), NSArray(), NSDictionary(), NSArray()]

for dict in things.flatMap( { $0 as? NSDictionary }) {

}

Pattern matching in a Swift for loop

You can combine a pattern matching conditional cast with a where clause like so:

let myStuff: [AnyObject] = [5, "dog", 11, 15, "cat"]

// item will be an Int, and divisible by 5
for case let item as Int in myStuff where item % 5 == 0 {
print(item)
}

// Prints:
// 5
// 15

How to assert matching types when iterating over an object?

It is worth using reduce instead of forEach in this case:

interface IMixed {
a: number;
b: string;
c: boolean;
}

const foo: IMixed = { a: 1, b: 'one', c: true };

const bar: IMixed = { a: 2, b: 'two', c: false };

(Object.keys(foo) as Array<keyof IMixed>)
.reduce((acc, elem) => ({
...acc,
[elem]: bar[elem]
}), foo);

Playground

Mutations does not work well in TypeScript.

See related questions:
first, second, third, forth and my article

UPDATE

(Object.keys(foo) as Array<keyof IMixed>).forEach(k => {
foo[k] = bar[k]; // error
});

You have an error here, because forEach as well as reduce is dynamic.
bar[k] is a union of number | string | boolean, as well as foo[k]. It means that inside the loop it would be possible to assign foo['a'] = foo['c']. Both values are valid, since we expect a union of number | string | boolean but it also would be unsafe. That's why TS forbids this behavior.

From the other hand, reduce works, because we create new object which extends IMixed instead of mutating

UPDATE

In order to be able to mutate foo you need add indexing to IMixed interface:

type IMixed= {
a: number;
b: string;
c: boolean;
[prop: string]: number | boolean | string
}

const foo: IMixed = { a: 1, b: 'one', c: true };

const bar: IMixed = { a: 2, b: 'two', c: false };


(Object.keys(foo) as Array<keyof IMixed>).forEach(k => {
foo[k] = bar[k];
});

Playground



Related Topics



Leave a reply



Submit