Vapor 4 - Approaching an issue - Return type of a function - ios

I have a function that triggers when I hit a certain endpoint, that contains some sort of a condition inside
if room exists {
return room
} else {
create a new room and return it
}
The issue is, that sometimes I return an EventLoopFuture (if the room doesn't exist), and sometimes I return a Room type without an EventLoopFuture (if a room exists), so I am not quite sure what the return type of the function should be.
Now the way I fix it is with a hacky workaround, where I always return an EventLoopFuture, and if the room exists, I update it and return it, which is totally unnecessary as there's nothing to update.
Would the right way be to save the newly created room and return it separately, so the condition will always return a Room type and not an EventLoopFuture?
The original function:
func findEmpty(req:Request) throws -> EventLoopFuture<Room> {
guard let category = req.parameters.get("category") else { throw Abort(.badRequest) }
try checkIfAllowed(category: category)
return Room.query(on: req.db)
.filter(\.$category == category)
.all()
.flatMap { relevantRooms in
for room in relevantRooms {
for isOccupied in room.availablePositions {
if !isOccupied {
return room.update(on: req.db)
.map {
return room
}
}
}
}
let newRoom = Room(...//initializing room)
return newRoom.save(on: req.db)
.map {
return newRoom
}
}
}

With the introduction of async, you can choose to return either the Future or the resolved value. However, in most cases, the async pattern results in simpler code. If you do want to return a Future of your room, then it should be as simple as replacing your existing return room with return req.eventLoop.makeSucceededFuture(room) instead of the unnecessary update. so:
return room.update(on: req.db).map {
return room
}
Becomes:
return req.eventLoop.makeSucceededFuture(room)
Converting code using Future pattern to async is relatively straightforward (and if it isn't that's probably a sign you should leave it as Future). In your case:
func findEmpty(req:Request) async throws -> Room {
guard let category = req.parameters.get("category") else { throw Abort(.badRequest) }
try checkIfAllowed(category: category)
let relevantRooms = try await Room.query(on: req.db)
.filter(\.$category == category)
.all()
for room in relevantRooms {
for isOccupied in room.availablePositions {
if !isOccupied {
return room
}
}
}
let newRoom = Room(...//initializing room)
try await newRoom.save(on: req.db)
return newRoom
}
Just an observation, but your inner for could be replaced with something simpler:
if let unoccupied = room.availablePositions.first { !$0.isOccupied } {
return unoccupied
}

Related

Conditional member access for map children?

Lets say you are trying to access deeply nested children in a map and you are not able to expect their parents to be there. Example:
Map awesomeMap = {
"this":{
"is":{
"sometimes":"not here"
}
}
}
Map notAwesomeMap = {
"this":{
"haha":{
"we":"switched"
}
}
}
When I go to access notAwesomeMap['this']['is']['sometimes'] it will return an error because ['this']['is'] is null, and you cannot look for the value ['sometimes'] of null.
So that's fine, but I was hoping to be able to use conditional member access operators...
notAwesomeMap['this']?.['is']?.['sometimes']
but that doesn't work...
Short of wrapping everything in a try block, is there a good way to handle these situations?
Edit: I tried playing around with this and I didn't find anything really illuminating, but maybe this gives someone an idea
void main() {
Map nestedMap = {
'this':{
'is':{
'sometimes':'here'
}
}
};
final mapResult = nestedMap['this'];
print(mapResult); //returns {is: {sometimes: here}}
final nullResult = nestedMap['this']['is an'];
print(nullResult); // returns null
final nullifiedResult = nullify(nestedMap['this']['is an']['error']);
print(nullifiedResult); // returns error, but is this possible another way?
final errorResult = nestedMap['this']['is an']['error'];
print(errorResult); // returns error
}
nullify(object){
try {
final result = object;
return result;
}
catch (e) {
return null;
}
}
One way would be
final result = (((nestedMap ?? const {})['this'] ?? const {})['is an'] ?? const {})['error'];
See also Null-aware operator with Maps
You could write a simple function to help do what you want:
R lookup<R, K>(Map<K, dynamic> map, Iterable<K> keys, [R defaultTo]);
Example usage:
final result = lookup(inputMap, ['this', 'is', 'something']);
Example implementation:
https://dartpad.dartlang.org/1a937b2d8cdde68e6d6f14d216e4c291
void main() {
var nestedMap = {
'this':{
'is':{
'sometimes':'here'
}
}
};
print(lookup(nestedMap, ['this']));
print(lookup(nestedMap, ['this', 'is']));
print(lookup(nestedMap, ['this', 'is', 'sometimes']));
print(lookup(nestedMap, ['this', 'is', 'error']));
// Bail out on null:
print(lookup(nestedMap, ['error'], 'Default Value'));
}
R lookup<R, K>(Map<K, dynamic> map, Iterable<K> keys, [R defaultTo]) {
dynamic current = map;
for (final key in keys) {
if (current is Map<K, dynamic>) {
current = current[key];
} else {
return defaultTo;
}
}
return current as R;
}
I like #matanlurey's approach, but have made two changes:
Drop the defaultTo since you can still use ?? which is more readable.
Swallow type-cast errors.
R lookup <R, K>(Map<K, dynamic> map, Iterable<K> keys) {
dynamic current = map;
for (final key in keys) {
if (current is Map<K, dynamic>) {
current = current[key];
}
}
try{
return current as R;
} catch(e){
// do nothing
}
}
Usage is similar
String someValue = lookup(nestedMap, ['some', 'value']) ?? 'Default Value';

Doubly linked list java remove

I have a problem when deleting many nodes.
I can delete them if I select nodes like this:
But if I do something like this, I cannot delete them:
My Code:
public boolean remove(ProductNode<E> data) {
if (isEmpty()) {
throw new NoSuchElementException();
}
for (ProductNode<E> current = this.head; current != null; current = current.next) {
ProductNode<E> pre = current.prev;
ProductNode<E> next = current.next;
if (data != null) {
if (current.data.equals(data.data)) {
if (pre == null) {
head = next;
current.next = null;
} else {
if (next != null) {
next.prev = pre;
}
}
if (next == null) {
pre.next = null;
current.prev = null;
tail = pre;
} else {
if (pre != null) {
pre.next = next;
}
}
}
}
}
size--;
return false;
}
Search node
public ProductNode<E> search(E data) {
for (ProductNode<E> current = this.head; current != null; current = current.next) {
if (current.data.equals(data)) {
return current;
}
}
return null;
}
Remove
public void remove(E e) {
remove(search(e));
}
Delete:
for(Tab_Product p : remove_list){
List_Products.list_products.remove(p);
}
Your remove function (ProductNode data), is a bit complicated and may be affecting your code's ability to delete multiple nodes. In the case of this remove function you do not need traverse the whole data set. If you already have a reference to the node you can just directly modify the list with it.
public boolean remove(ProductNode<E> data) {
if (isEmpty()) {
throw new NoSuchElementException();
}
ProductNode<E> pre = data.prev;
ProductNode<E> next = data.next;
//First remove the nodes references to its neighbors.
data.prev = null;
data.next = null;
// Now check the neighbors and update their references
// to remove all references to the deleted node.
if (pre != null) pre.next = next;
if (next != null) next.prev = pre;
if (data == head) { //This checks the actual memory address.
head = next;
}
size--;
}
Since you already have the ProductNode, you do not need to search the list. your search() function is already doing that for you. since you already have the node you just need to make its references to its neighbors null then you just have to access the neighbors (if there are any) and make their old references skip over the deleted node.
I noticed a few reference errors where a deleted node was not getting completely removed from the list but i will not mention them because this delete function is rather complicated. Try simplifying the delete function and then see what your results are.
It also might be helpful if you show us the structure of the List_Products object.
Additionally you should verify that the data you select in the UI is getting passed correctly. This could be a UI bug.

Questions about using Futures and Completers

Hey have I read all I can find about futures, but I would like some more advice on proper usage.
I am writing an API library, that bridges the gap between HTTP Requests and the app. So I use the future returned by HTTP in most cases, however sometimes the data is already retrieved. Is that the appropriate time to use a Completer?
ex.
String _someData = "";
Future<String> getSomeData(){
if (_someData == ""){
return Api.getSomeData().then((String someData){
_someData = someData;
return _someData;
});
} else {
var completer = new Completer();
completer.complete(_someData);
return completer.future;
}
}
-edit- Also if I create a Completer, but end up not using its future or calling complete. Will that cause a mem leak? Should I call its complete method or dispose of it somehow?
Thanks :)
You can use the named constructor Future.value if the value is immediately accessible. You won't need a Completer.
String _someData = "";
Future<String> getSomeData(){
if (_someData == ""){
return Api.getSomeData().then((String someData){
_someData = someData;
return _someData;
});
} else {
return new Future.value(_someData);
}
}
And for your second question, if you create a Completer without using it, I guess the garbage collector will simply free its memory when there won't be anymore references to it in your code.
Use an async function instead.
import "dart:async";
String _someData = "";
Future<String> getSomeData() async {
if (_someData == "") {
_someData = await Api.getSomeData();
}
return _someData;
}
Compiler generates approximately the following code:
import "dart:async";
String _someData = "";
Future<String> getSomeData() {
var $_awaiter = new Completer<String>();
try {
if (_someData == "") {
Api.getSomeData().then(($) {
$_awaiter.complete($);
}).catchError((e, s) {
$_awaiter.completeError(e, s);
});
return $_awaiter.future;
} else {
$_awaiter.complete(_someData);
return $_awaiter.future;
}
} catch (e, s) {
$_awaiter.completeError(e, s);
return $_awaiter.future;
}
}

Logic inside List.map

I want to include logic inside my list mapping. Example:
I can do something like this:
List projects = urls.map((url) => url.substring(0, 3)).toList();
But can I, somehow, do something like this:
List projects = urls.map((url) {
if (url.indexOf("?") == -1) {
url;
} else {
url.substring(0, url.indexOf("?"));
}
}).toList();
Of course you can. What is missing is a return statement.
With the short function format (=>) return is implicit and the result of the expression gets returned. If you use the function block format you have to explicitly return the value you want to have in the result. Without an explicit return null is returned.
List projects = urls.map((url) {
if (url.indexOf("?") == -1) {
return url;
} else {
return url.substring(0, url.indexOf("?"));
}
}).toList();

Twitter4J, documentation about possible fields

I'm starting working with Twitter4J and I would like to figure out what each method does.
I'm implementing UserStreamListener which has a lot of methods what don't have JavaDoc,, as:
public void onException(Exception ex) {
public void onBlock(User arg0, User arg1)
public void onDeletionNotice(long arg0, long arg1)
public void onDirectMessage(DirectMessage arg0)
...
So, I don't know when they are exactly executed and what the params mean. I downloaded the code of Twitter4J and I have seen a class where you can see how the library is generating the events, but when I try to link that information with Twitter, I don't find more information.
I found this web https://dev.twitter.com/docs/platform-objects/tweets , but it isn't this information.
public final class JSONObjectType {
public enum Type {
SENDER,
STATUS,
DIRECT_MESSAGE,
DELETE,
LIMIT,
STALL_WARNING,
SCRUB_GEO,
FRIENDS,
FAVORITE,
UNFAVORITE,
FOLLOW,
UNFOLLOW,
USER_LIST_MEMBER_ADDED,
USER_LIST_MEMBER_DELETED,
USER_LIST_SUBSCRIBED,
USER_LIST_UNSUBSCRIBED,
USER_LIST_CREATED,
USER_LIST_UPDATED,
USER_LIST_DESTROYED,
USER_UPDATE,
BLOCK,
UNBLOCK,
DISCONNECTION,
UNKNOWN
}
private static final Logger logger = Logger.getLogger(JSONObjectType.class);
/**
* Determine the respective object type for a given JSONObject. This
* method inspects the object to figure out what type of object it
* represents. This is useful when processing JSON events of mixed type
* from a stream, in which case you may need to know what type of object
* to construct, or how to handle the event properly.
*
* #param json the JSONObject whose type should be determined
* #return the determined JSONObjectType, or null if not recognized
*/
public static Type determine(JSONObject json) {
// This code originally lived in AbstractStreamImplementation.
// I've moved it in here to expose it as a public encapsulation of
// the object type determination logic.
if (!json.isNull("sender")) {
return Type.SENDER;
} else if (!json.isNull("text")) {
return Type.STATUS;
} else if (!json.isNull("direct_message")) {
return Type.DIRECT_MESSAGE;
} else if (!json.isNull("delete")) {
return Type.DELETE;
} else if (!json.isNull("limit")) {
return Type.LIMIT;
} else if (!json.isNull("warning")) {
return Type.STALL_WARNING;
} else if (!json.isNull("scrub_geo")) {
return Type.SCRUB_GEO;
} else if (!json.isNull("friends")) {
return Type.FRIENDS;
} else if (!json.isNull("event")) {
String event;
try {
event = json.getString("event");
if ("favorite".equals(event)) {
return Type.FAVORITE;
} else if ("unfavorite".equals(event)) {
return Type.UNFAVORITE;
} else if ("follow".equals(event)) {
return Type.FOLLOW;
} else if ("unfollow".equals(event)) {
return Type.UNFOLLOW;
} else if (event.startsWith("list")) {
if ("list_member_added".equals(event)) {
return Type.USER_LIST_MEMBER_ADDED;
} else if ("list_member_removed".equals(event)) {
return Type.USER_LIST_MEMBER_DELETED;
} else if ("list_user_subscribed".equals(event)) {
return Type.USER_LIST_SUBSCRIBED;
} else if ("list_user_unsubscribed".equals(event)) {
return Type.USER_LIST_UNSUBSCRIBED;
} else if ("list_created".equals(event)) {
return Type.USER_LIST_CREATED;
} else if ("list_updated".equals(event)) {
return Type.USER_LIST_UPDATED;
} else if ("list_destroyed".equals(event)) {
return Type.USER_LIST_DESTROYED;
}
} else if ("user_update".equals(event)) {
return Type.USER_UPDATE;
} else if ("block".equals(event)) {
return Type.BLOCK;
} else if ("unblock".equals(event)) {
return Type.UNBLOCK;
}
} catch (JSONException jsone) {
try {
logger.warn("Failed to get event element: ", json.toString(2));
} catch (JSONException ignore) {
}
}
} else if (!json.isNull("disconnect")) {
return Type.DISCONNECTION;
}
return Type.UNKNOWN;
}
}
For example, if the JSON is coming "event" and "block", the method onBlock will be executed. But, where is the information about all the possible fields in a Tweet???
You may find the information you need in Twitter's Streaming message types documentation, specifically, take a look at the Events section which describes the message format:
{
"target": TARGET_USER,
"source": SOURCE_USER,
"event":"EVENT_NAME",
"target_object": TARGET_OBJECT,
"created_at": "Sat Sep 4 16:10:54 +0000 2010"
}
Additionally, this documentation for UserStreamListener will help with the argument names of the methods, for example the onBlock method:
onBlock(User source, User blockedUser)

Resources