In the popular board game Monopoly, players have the opportunity to buy/trade different properties, and when they monopolize a certain neighborhood, they can build houses. I am trying to express all of these properties as a nested enumeration in Swift, but when it comes to expressing a player's properties in an Array, I'm stumped.
Here is what I have tried so far.
enum Property {
enum Brown {
case mediterranean, baltic
}
enum LightBlue {
case oriental, vermont, connecticuit
}
enum pink {
case stCharles, states, virginia
}
...
}
var properties: [Property] = [
Property.Brown.baltic, // ERROR: Cannot convert value of type 'Property.Brown' to expected element type 'Property'
Property.Brown.mediterranean
]
As you can see, I cannot store these Properties in a [Property] Array, because a Property.Brown isn't a Property (understandable). What would I need to change to be able to store Property.<Insert Neighborhood Here>s in an array? I understand that an [Any] would work, but I am concerned about type safety, so this won't work.
Edit 05 July 2018, 13:18 PDT
I am writing a program that will act as the bank for a Monopoly game and need a way to determine which players (or the bank) owns properties. I'm currently Playgrounding and prototyping to figure out what works for me.
As you know,
because a Property.Brown isn't a Property (understandable).
You may need a type common to your Property.Brown, Property. LightBlue, ...
Maybe you can use a protocol just for storing them in an Array:
protocol PropertyEnums {}
enum Property {
enum Brown: PropertyEnums {
case mediterranean, baltic
}
enum LightBlue: PropertyEnums {
case oriental, vermont, connecticuit
}
//...
}
var properties: [PropertyEnums] = [
Property.Brown.baltic,
Property.Brown.mediterranean,
//...
]
But I cannot be sure if this might be the best solution for you, as you are not showing the use cases of properties.
Something like this might be better for some use cases:
enum Property {
enum Brown {
case mediterranean, baltic
}
enum LightBlue {
case oriental, vermont, connecticuit
}
//...
case brown(Brown)
case lightBlue(LightBlue)
//...
}
var properties: [Property] = [
.brown(.baltic),
.brown(.mediterranean),
//...
]
Please show us how do you want to use your properties.
Related
I want to check if a value exists in an array of objects.
My array looks something like this:
[
0: {
id: 'unique_obj_id',
item: {
id: 'unique_item_id',
...
},
...
},
1: {...}
]
The objects in the array can be one of many interface types (depending on my api call, here: resource strings represent these interfaces, which are defined somewhere else). But one array will always consist of the same interface types for a given data request.
I'm trying to write a reusable function to check whether given_id exists for any object in the array for obj.item.id.
So far I've managed to write the correct logic but typescript throws some exceptions that I can't seem to figure out. shopApprovalData is a collection of interfaces each following the above object structure accessible by the indices of resource.
export type ApprovalResource = 'str1' | 'str2' | 'str3' | 'str4' | 'str5';
export const checkApprovalItem = (given_id: string, resource: ApprovalResource) => {
const shopApprovalData = useShopApprovals();
if (shopApprovalData && shopApprovalData[resource]) {
const resourceApprovalData = shopApprovalData[resource];
if (resourceApprovalData.find(e => e.item.id === id)) {
return true;
}
}
return false;
}
Typescript shows me that shopApprovalData and itemApprovalData is possibly undefined and that the expression find() is not callable since signatures of members of the union type are not compatible with each other. (Apparently, some removes the error, why?)
What approach should I choose instead to check whether the given_id exists in any object of the array?
Based on your usecase, i create this code sandbox: https://codesandbox.io/s/stackoverflow-answer-ke9drk?file=/src/index.ts
explanation:
Type wont compile, so we need something to mark what the interface of the object.
Also i may confused by your code shopApprovalData[resource] what do you want to achieve here? for example, if resource is str1 shopApprovalData[resource] will always return undefined because no index str1 in Array
I hope it help,
Best Regards
So I'm trying to learn writing more generic. I have POST/PUT/DELETE/GET for attatchments together with these entity types (there are also alot more entity types that I will need to add in the future).
This code works, and it is indeed more generic than writing 4 different getAttatchment with hardcorded personnel/workoder etc where it is ${apiType} currently. Some progress made atleast.
But how could I rewrite this even better? I would like to have e.g getAttatchments<T>(EntityType<T> .....) but I don't know how I could write it like that. And ofcourse it need to be typesafe so you don't actually pass entities that does not exist.
export enum EntityType {
Personnel = 'personnel',
Workorder = 'workorder',
Asset = 'asset',
AssetBooking = 'assetbooking',
}
async getAttachments(id: string | number, apiType: EntityType) {
return await this.get<AttachmentDetails[]>(
`attachment/${apiType}/${id}/attachment`
)
}
What you have already looks fairly generic but I would forgo the enum and use a union type instead. e.g.
type EntityType = 'personnel'|'workorder'|'asset'|'assetbooking'
type AttachmentDetails= any // whatever
// mock implementation
function get<T>( url:string) {
return ''
}
function getAttachments<T extends EntityType>(id: string | number, apiType: T) {
return get<AttachmentDetails[]>( `attachment/${apiType}/${id}/attachment` )
}
Playground
There are two simple problems here in initializing this structure.
One is the enunumerated value TS (I get error : Cannot convert value of type 'TournNames' to expected argument type 'TournamentName')
the other is initializing an array of strings (I get the error : Cannot convert value of type '[String]' to expected argument type 'TouramentScores'
Suppose I am trying to set up a structure to model the scores of tennis players and all of their matches in each of the major tournaments (just for fun). Each tournament has a name (e.g. Wimbledon) and a series of scores for that player (for example, in the opening match, their score might be "4-6, 6-4, 7-6, 6-2")... upto 7 matches in each tournament. Each player should have an array of four tournaments (names and array of scores), and eventually there should be an array of players. I am also trying to use enums not too successfully. Ideally, if I want to find how Roger Federer did in his third match of wimbledon this year, I would access something like player.tournament.wim.Roundof32 or something roughly like that. But before I can even get to playing with that, I can't seem to init dummy data for even a single tournament.
Any ideas? I don't think this is that hard of question but I just don't know each. See "*** this line" below for two lines that are problematic
// tournament name enum
enum TournNames : String {
case wim = "Wimbledom"
case fo = "French Open"
case ao = "Australian Open"
case uo = "US Open"
}
//
struct TournamentName {
var Tname : TournNames // = .wim
}
// This is the structure for a tournament score array with some dummy values.
struct TouramentScores {
var Match1 : String = "7-6, 6-4, 3-6, 7-6"
var Match2 : String = "7-6, 6-4, 3-6, 7-6"
}
// This is one entire Tournament record for one player = tournament name + array of scores ... the next goal but not used here until I get over these hurdles
struct TournamentResult {
var TournamentName : TournNames = .wim
var Scores : TouramentScores
}
// ... finally the structure of a player ...
struct DummyTennisPlayer {
var LastName : String // last name
var FirstName : String //first name
var TN : TournamentName
var TS : TouramentScores
// var WimRes : TournamentResult // to start a single tournament
// var SeasonResults : [TournamentResult] // ultimately should be an array of 4 tournaments
}
// trying to initialize some dummy data without success after trying several things
extension DummyTennisPlayer {
static var dummyResults : [DummyTennisPlayer] {
[
DummyTennisPlayer.init(
LastName : "Federer",
FirstName: "Roger",
TN : TournNames.wim // **** this line
,
TS : ["XX", "yy"] /// *** this line
)
]
}
}
As I think you're discovering, a simple series of nested types is unlikely to cut it here. As soon as you get to entities like players, tournaments, matches and lookups like "how Roger Federer did in his third match of wimbledon this year", you've become a candidate for using a database where you can manipulate one-to-many and many-to-many relationships. I can't tell you what database to use, and anyway that's a matter of opinion; from what you've said so far, SQLite would be sufficient (and I am personally not a fan of Core Data just for this kind of thing).
I guess your code is a kind of exercise, so before you go on later to Core Data or SQLite,
extension DummyTennisPlayer {
static var dummyResults: [DummyTennisPlayer] = [
DummyTennisPlayer(LastName: "Federer", FirstName: "Roger", WimbledomResult: TournamentResult(Scores: TouramentScores()))
]
}
should answer your question.
1 - To initialize a Swift struct, use the following syntax:
MyStruct(property1: "my property1 value", property2: "my property2 value")
2 - the tournament name property in TournamentResult is already set to .wim so you just need to initialize the Scores. As your TournamentScores properties are already all set, you just need to pass an instance of TournamentScores() to TournamentResult(Scores:).
By the way, only use lowercases for the first letter of the name of your variables or struct properties: var lastName or TournamentResult(scores:).
I think you are confusing the term "multi-dimensional (array) structures" (which are just arrays nested inside other arrays, like that: [ [1, 2, 3], [2, 3, 4], [3, 4, 5]]) with the struct objects. You are probably not supposed to use structs so extensively here.
Don't hesitate to review the way you decide to use enums, structs, or arrays. Your code may work but will be difficult to read and use (example: how would you access a specific set score if you put all of the set scores in a single String? Why not use an array?)
I need to map enum values to 1 variable and then call the variable and check if that enum was present or not.
render() {
const {
id,
name,
features} = this.props;
}
Features would be the variable that needs to be mapped according to which enums are coming in. I would get something similar from the API:
{"id":"111", "name":"jack", "features":["MOUNTAIN", "HILL"]}
So there would be a total of 4 different features : MOUNTAIN, HILL, LAKE, FISH.
Then when needed I can check:
if(features.mountain)
//do stuff
If you like to check if a given property from your enum (for example "MOUNTAIN") is included in the features array returned from the api you can use the Array.prototype.includes() method:
if(featuresArrayfromApi.includes('MOUNTAIN'){
//do stuff
}
If you would like to check if the features returned from the api include one or more of the properties in your features enum you can combine includes with Array.prototype.some().
For example, in Typescript you would write it like this:
enum Features { MOUNTAIN, HILL, LAKE, FISH }
if(Object.keys(Features)
.some(feature => featuresFromApi.includes(feature))){
// do stuff
}
Edit
The features key from the api data should be mapped like any other key(id, name) - just instead of holding 1 value it holds an array. Then you can use the validations suggested above in an if clause. For example:
const data = [
{"id":"111", "name":"jack", "features":["MOUNTAIN", "HILL"]},
{"id":"222", "name":"john", "features":["FISH", "HILL", "LAKE"]}
{"id":"333", "name":"joe", "features":["LAKE", "HILL", "FISH"]}
]
data.map(record =>{
console.log(record.id);
console.log(record.name);
if (record.features.includes('MOUNTAIN'){
// do stuff
}
})
Also, bear in mind that enum is a Typescript symbol which isn't available in Javascript, so in case you are not using Typescript you can just declare it like this and it would work the same:
const Features = {
MOUNTAIN: "MOUNTAIN",
HILL: "HILL",
LAKE, "LAKE",
FISH: "FISH"
}
Is there any way of achieving type variety in indexable type interfaces?
I have a state which contains fields with different types.
public state:State = {
item: {...},
item1: {...},
...
itemCollection: [],
itemCollection1: []
...
}
In the code above item is some specific name which I want to access to the state with it like this.state['item']. But, as far as I realized, indexable types in typescript only accept values with the exact same type.
I have an interface like:
interface State{
[field:string]: ITEM | ITEM[] | null
}
, which I want to be able to turn into something like this:
interface State{
[field:string]: ITEM | null
[field:string]: ITEM[] | null
}
I know it looks stupid. As I am new to TypeScript, I wonder if I could do it like I used to do in JavaScript.
Explanation
indexable types in typescript only accept values with the exact same
type
That's very close, but not exactly correct — the index signature must contain all types that appear on the right-hand side. It means the type used for indexing can be exactly the same as your values, but can also be wider.
What does it mean for a type to be wider? You can think of types are sets. For example, a specific string like "foo" belongs to the set of all strings, so string is wider than "foo". The types at the top (any and unknown) contain almost everything, meaning everything can be assigned to them.
Solution
You must use an index signature that contains all possible types of the values.
interface State {
[field: string]: ITEM | ITEM[] | null
}
or
interface State {
[field: string]: any;
}
However, this will destroy any code completion. If it suits your use case, you could also try this approach:
type Foo = {
foo: number[]
bar: string[]
[index: string]: unknown;
}
const foo: Foo = {
foo: [1, 2, 3],
bar: ["a", "b", "c"],
baz: Window
}
In this example, we get correct code hints for foo and bar, and any excess properties are of type unknown. This means they need to be verified before they can be used.
let unrecognized = foo["baz"]
if (unrecognized instanceof Window) {
console.log(unrecognized.document);
}