Merge of two enums zod - zod

I have 2 enums, const Option1 = z.enum(["option1"]) and const Option2 = z.enum(["option2"]).
I want to merge these two into z.ZodEnum<["option1", "option2"]>
The only way I came up with so far is
export const Options = z.enum([
...Option1.options,
...Option2.options,
]);
// Options.options is now ["option1", "option2"]
Is there any zod native way to do this?

the issue you're facing here is due to the type of the combined options.
const allOptions = [...Option1.options, ...Option2.options]
the inferred type of allOptions in this case is: ("option1" | "option2")[] and zod can't create an enum out of that.
however, if you define allOptions like this:
const allOptions = [...Option1.options, ...Option2.options] as const
then the inferred type of allOptions will be the tuple ["option1", "option2"] which is exactly what you want.
so, putting that all together, to combine the options, you'd say:
const Options = z.enum([...Option1.options, ...Option2.options] as const)
and the inferred type of Options will be z.ZodEnum<["option1", "option2"]>

Related

Destructure a bunch of variables from an object

I have an object in Javascript as follows:
profile= {
profilePicture: null,
username: null,
age: null,
gender: null,
followedBy: [],
following: [],
aboutMe: null
}
And I would like to destructure all of the properties into variables, like such:
const {userName, age, gender, followedBy, following, aboutMe} = profile
Let's say in the future this profile object will have 30 properties, how can I iterate over all the properties instead of typing them one by one? I was thinking something like this
const {userName, age, ...rest} = profile
but instead of the rest variable, it actually gives me all the other property names as stand-alone variable names that I can use. I tried something like
const {...Object.keys(profile)} = profile
but it's not working.
Any ideas?
Simply change the curly bracket to square bracket
For Example
const [userName, age, gender, followedBy, following, aboutMe] = profile
const [userName, age, ...rest] = profile

How can I join two Firestore queries using rxfire and rxjs (OR query)

The goal is simple: to join two firestore queries utilizing rxjs, rxfire, and the rnfirebase react native library.
I've read multiple tutorials 1, 2 on joining queries, but they all fail with different errors.
//Simple test for collectionData
import { collectionData } from 'rxfire/firestore';
this.myQuery = this.props.docRef.collection(`messages`).where('read', 'array-contains', this.props.me.uid)
collectionData(this.myQuery, 'id').subscribe(docs => console.log(docs))
//Fails with error: this._next is not a function.
Alternatively,
this.publicQuery = this.props.docRef.collection('messages').where('public', '==', true)
this.myQuery = this.props.docRef.collection(`messages`).where('read', 'array-contains', this.props.me.uid)
const myQuery$ = new Rx.Subject();
const publicQuery$ = new Rx.Subject();
this.myQuery.onSnapshot((querySnapshot) => {
myQuery$.next(querySnapshot.docs.map(d => d.data() ));
});
this.publicQuery.onSnapshot((querySnapshot) => {
publicQuery$.next(querySnapshot.docs.map(d => d.data() ));
});
const orQuery$ = combineLatest(this.myQuery, this.publicQuery).switchMap((docs) => {
var [one, two] = docs;
var combined = one.concat(two);
return Rx.Observable.of(combined);
})
orQuery$.subscribe((result) => {
console.log('>>>> ', result)
})
//TypeError: undefined is not a function (near ...switchMap)
How can I successfully join two firestore queries (OR)?
You're already very close to the solution. Let's go through the issues step-by-step.
First, it's not necessary to create a Subject just to transform your result from onSnapshot. Instead of this:
this.myQuery.onSnapshot((querySnapshot) => {
myQuery$.next(querySnapshot.docs.map(d => d.data()))
});
We can achieve the same using 'pipeable transformation operators':
const myQuery$ = this.myQuery.onSnapshot.pipe(
map(querySnapshot => querySnapshot.docs.map(d => d.data()))
);
The same goes for the other query:
const publicQuery$ = this.publicQuery.onSnapshot.pipe(
map(querySnapshot => querySnapshot.docs.map(d => d.data())
);
Second, to join those two queries, combineLatest is indeed the correct creation function.
However, your error might result from you using a newer RxJS version, that doesn't support 'fluent' operators (officially called "patch operators") anymore. They have been replaced by 'pipeable operators' from RxJS 6 onwards. As an example, myObs$.map(...) has become myObs$.pipe(map(...)). The tutorials probably use an older version of RxJS where the first is still possible.
Also, it shouldn't be necessary to use switchMap if the inner Observable is just an of operator. It is sufficient in this case to use the map operator, which will behave equally.
Using the new RxJS 6+ syntax together with map, the combination will look like this:
const orQuery$ = combineLatest(myQuery$, publicQuery$).pipe(
map(([one, two]) => one.concat(two))
)
The rest of your code should be correct.
Side Note: Keep in mind that the equivalent of your code in SQL is UNION (not JOIN). In order to JOIN programatically, you'd need to combine each object of result set A with each object of result set B and create a joined object for each pair. Such a function for a keyless OUTER JOIN would look like this (placed in your map pipe):
one.map(a =>
two.map(b => Object.assign({}, a, b)))
.reduce((p, c) => p.concat(c), [])
If you want to have a UNION with no duplicate objects, concat only those items from two that have no matching primary key in list one. This would be your mapping function:
one.concat(two.filter(twoItem => !one.some(oneItem => oneItem.id == twoItem.id)))
DEMO: A complete, working demo with the above code and a simulated FireStore can be found here:
https://stackblitz.com/edit/rxjs-mefynu?devtoolsheight=60

Pick one of union

I am trying to use the type ImageURISource which is here - https://github.com/facebook/react-native/blob/26684cf3adf4094eb6c405d345a75bf8c7c0bf88/Libraries/Image/ImageSource.js#L15
type ImageURISource = {
uri?: string,
bundle?: string,
method?: string,
headers?: Object,
body?: string,
cache?: 'default' | 'reload' | 'force-cache' | 'only-if-cached',
width?: number,
height?: number,
scale?: number,
};
export type ImageSource = ImageURISource | number | Array<ImageURISource>;
However we see that it is exported as a union along with 2 other things. Is it possible to pick from a union just one?
I was hoping to do:
$Pick<ImageSource, ImageURISource>
It's not very pretty, but you could use refinement to specifically refine the type that you want out of it by doing something like this:
var source: ImageSource = {}
if (typeof source === "number" || Array.isArray(source)) throw new Error();
var uriSource = source;
type ImageURISource = typeof uriSource;
The downside here is that if the add more types to the union, your code would start failing again.
It seems like you'd be best off making a PR to react-native to expose that type.

Immutable JS - merge creates a list of map objects from array

I am using redux and immutablejs, and I am trying to create a reducer function.
I have come across some behaviour I did not expect
const a = new Immutable.Map({
numbers: [{id: 1},{id: 2}]
});
const b = a.merge(
{
numbers: [{id: 4},{id: 5}]
}
);
Here are the values of a and b
a.get("numbers");
[Object, Object]
b.get("numbers");
List {size: 2, _origin: 0, _capacity: 2, _level: 5, _root: null…}
b.get("numbers").get(0);
Map {size: 1, _root: ArrayMapNode, __ownerID: undefined, __hash: undefined, __altered: false}
I did not expect numbers to be an immutable List of Map objects.
In my application, using redux, I set the initial state to:
const initialState = new Immutable.Map({
error: null,
isBusy: false,
films: []
});
I the reducer, when I fetch films I try to merge them as follows:
return state.merge({
isBusy: false,
films: action.payload,
error: null
});
This causes issues in the react component, as films are initially an array of objects, and then they become an Immutable List of Maps.
Should I create a different initial state, or should I be using a different type of merge? Or something else?
Thanks
I think what you are trying to do is not merge of whole map object, at least should not be in the case you say, it should be update + ( concat or merge ):
const a = new Immutable.Map({
numbers: [{id: 1},{id: 2}]
});
const b = a.update("numbers", numbers =>
numbers.concat([{id: 4},{id: 5}])
// or
numbers.merge([{id: 4},{id: 5}])
);
by doing merge in your code, you are overriding the existing ones due to the nature of "merge" because the keys are the same in the merge; "numbers".

Dynamic properties in Scala

Does Scala support something like dynamic properties? Example:
val dog = new Dynamic // Dynamic does not define 'name' nor 'speak'.
dog.name = "Rex" // New property.
dog.speak = { "woof" } // New method.
val cat = new Dynamic
cat.name = "Fluffy"
cat.speak = { "meow" }
val rock = new Dynamic
rock.name = "Topaz"
// rock doesn't speak.
def test(val animal: Any) = {
animal.name + " is telling " + animal.speak()
}
test(dog) // "Rex is telling woof"
test(cat) // "Fluffy is telling meow"
test(rock) // "Topaz is telling null"
What is the closest thing from it we can get in Scala? If there's something like "addProperty" which allows using the added property like an ordinary field, it would be sufficient.
I'm not interested in structural type declarations ("type safe duck typing"). What I really need is to add new properties and methods at runtime, so that the object can be used by a method/code that expects the added elements to exist.
Scala 2.9 will have a specially handled Dynamic trait that may be what you are looking for.
This blog has a big about it: http://squirrelsewer.blogspot.com/2011/02/scalas-upcoming-dynamic-capabilities.html
I would guess that in the invokeDynamic method you will need to check for "name_=", "speak_=", "name" and "speak", and you could store values in a private map.
I can not think of a reason to really need to add/create methods/properties dynamically at run-time unless dynamic identifiers are also allowed -and/or- a magical binding to an external dynamic source (JRuby or JSON are two good examples).
Otherwise the example posted can be implemented entirely using the existing static typing in Scala via "anonymous" types and structural typing. Anyway, not saying that "dynamic" wouldn't be convenient (and as 0__ pointed out, is coming -- feel free to "go edge" ;-).
Consider:
val dog = new {
val name = "Rex"
def speak = { "woof" }
}
val cat = new {
val name = "Fluffy"
def speak = { "meow" }
}
// Rock not shown here -- because it doesn't speak it won't compile
// with the following unless it stubs in. In both cases it's an error:
// the issue is when/where the error occurs.
def test(animal: { val name: String; def speak: String }) = {
animal.name + " is telling " + animal.speak
}
// However, we can take in the more general type { val name: String } and try to
// invoke the possibly non-existent property, albeit in a hackish sort of way.
// Unfortunately pattern matching does not work with structural types AFAIK :(
val rock = new {
val name = "Topaz"
}
def test2(animal: { val name: String }) = {
animal.name + " is telling " + (try {
animal.asInstanceOf[{ def speak: String }).speak
} catch { case _ => "{very silently}" })
}
test(dog)
test(cat)
// test(rock) -- no! will not compile (a good thing)
test2(dog)
test2(cat)
test2(rock)
However, this method can quickly get cumbersome (to "add" a new attribute one would need to create a new type and copy over the current data into it) and is partially exploiting the simplicity of the example code. That is, it's not practically possible to create true "open" objects this way; in the case for "open" data a Map of sorts is likely a better/feasible approach in the current Scala (2.8) implementation.
Happy coding.
First off, as #pst pointed out, your example can be entirely implemented using static typing, it doesn't require dynamic typing.
Secondly, if you want to program in a dynamically typed language, program in a dynamically typed language.
That being said, you can actually do something like that in Scala. Here is a simplistic example:
class Dict[V](args: (String, V)*) extends Dynamic {
import scala.collection.mutable.Map
private val backingStore = Map[String, V](args:_*)
def typed[T] = throw new UnsupportedOperationException()
def applyDynamic(name: String)(args: Any*) = {
val k = if (name.endsWith("_=")) name.dropRight(2) else name
if (name.endsWith("_=")) backingStore(k) = args.first.asInstanceOf[V]
backingStore.get(k)
}
override def toString() = "Dict(" + backingStore.mkString(", ") + ")"
}
object Dict {
def apply[V](args: (String, V)*) = new Dict(args:_*)
}
val t1 = Dict[Any]()
t1.bar_=("quux")
val t2 = new Dict("foo" -> "bar", "baz" -> "quux")
val t3 = Dict("foo" -> "bar", "baz" -> "quux")
t1.bar // => Some(quux)
t2.baz // => Some(quux)
t3.baz // => Some(quux)
As you can see, you were pretty close, actually. Your main mistake was that Dynamic is a trait, not a class, so you can't instantiate it, you have to mix it in. And you obviously have to actually define what you want it to do, i.e. implement typed and applyDynamic.
If you want your example to work, there are a couple of complications. In particular, you need something like a type-safe heterogenous map as a backing store. Also, there are some syntactic considerations. For example, foo.bar = baz is only translated into foo.bar_=(baz) if foo.bar_= exists, which it doesn't, because foo is a Dynamic object.

Resources