What .Net object corresponds to an IronRuby hash - hash

I want to call a C# method from an IronRuby script that results in a Ruby hash. I tried Dictionary but that stays as is.
public Dictionary<string, string> GetHash()
{
Dictionary<string, string> hash = new Dictionary<string, string>();
hash.Add("a", "1");
hash.Add("b", "2");
return hash;
}
I'd like to be able to use it in my IronRuby script as a hash
myHash = scopeObj.GetHash()
myHash.each{ |k,v|
print("#{k}=#{v}")
}
The results of which are:
[a, 1]=
[b, 2]=

It doesn't work like that since the items in a .NET dictionary are KeyValuePair instances.
You can workaround that pretty easily, with a single line of conversion code:
d = scopeObj.get_hash
h = Hash[*d.collect { |x| [x.key,x.value] }.flatten]
h.each { |k,v| puts k,v }

Related

Use mongodb BsonSerializer to serialize and deserialize data

I have complex classes like this:
abstract class Animal { ... }
class Dog: Animal{ ... }
class Cat: Animal{ ... }
class Farm{
public List<Animal> Animals {get;set;}
...
}
My goal is to send objects from computer A to computer B
I was able to achieve my goal by using BinaryFormatter serialization. It enabled me to serialize complex classes like Animal in order to transfer objects from computer A to computer B. Serialization was very fast and I only had to worry about placing a serializable attribute on top of my classes. But now BinaryFormatter is obsolete and if you read on the internet future versions of dotnet may remove that.
As a result I have these options:
Use System.Text.Json
This approach does not work well with polymorphism. In other words I cannot deserialize an array of cats and dogs. So I will try to avoid it.
Use protobuf
I do not want to create protobuf map files for every class. I have over 40 classes this is a lot of work. Or maybe there is a converter that I am not aware of? But still how will the converter be smart enough to know that my array of animals can have cats and dogs?
Use Newtonsoft (json.net)
I could use this solution and build something like this: https://stackoverflow.com/a/19308474/637142 . Or even better serialize the objects with a type like this: https://stackoverflow.com/a/71398251/637142. So this will probably be my to go option.
Use MongoDB.Bson.Serialization.BsonSerializer Because I am dealing with a lot of complex objects we are using MongoDB. MongoDB is able to store a Farm object easily. My goal is to retrieve objects from the database in binary format and send that binary data to another computer and use BsonSerializer to deserialize them back to objects.
Have computer B connect to the database remotely. I cannot use this option because one of our requirements is to do everything through an API. For security reasons we are not allowed to connect remotely to the database.
I am hopping I can use step 4. It will be the most efficient because we are already using MongoDB. If we use step 3 which will work we are doing extra steps. We do not need the data in json format. Why not just sent it in binary and deserialize it once it is received by computer B? MongoDB.Driver is already doing this. I wish I knew how it does it.
This is what I have worked so far:
MongoClient m = new MongoClient("mongodb://localhost:27017");
var db = m.GetDatabase("TestDatabase");
var collection = db.GetCollection<BsonDocument>("Farms");
// I have 1s and 0s in here.
var binaryData = collection.Find("{}").ToBson();
// this is not readable
var t = System.Text.Encoding.UTF8.GetString(binaryData);
Console.WriteLine(t);
// how can I convert those 0s and 1s to a Farm object?
var collection = db.GetCollection<RawBsonDocument>(nameof(this.Calls));
var sw = new Stopwatch();
var sb = new StringBuilder();
sw.Start();
// get items
IEnumerable<RawBsonDocument>? objects = collection.Find("{}").ToList();
sb.Append("TimeToObtainFromDb: ");
sb.AppendLine(sw.Elapsed.TotalMilliseconds.ToString());
sw.Restart();
var ms = new MemoryStream();
var largestSixe = 0;
// write data to memory stream for demo purposes. on real example I will write this to a tcpSocket
foreach (var item in objects)
{
var bsonType = item.BsonType;
// write object
var bytes = item.ToBson();
ushort sizeOfBytes = (ushort)bytes.Length;
if (bytes.Length > largestSixe)
largestSixe = bytes.Length;
var size = BitConverter.GetBytes(sizeOfBytes);
ms.Write(size);
ms.Write(bytes);
}
sb.Append("time to serialze into bson to memory: ");
sb.AppendLine(sw.Elapsed.TotalMilliseconds.ToString());
sw.Restart();
// now on the client side on computer B lets pretend we are deserializing the stream
ms.Position = 0;
var clones = new List<Call>();
byte[] sizeOfArray = new byte[2];
byte[] buffer = new byte[102400]; // make this large because if an document is larger than 102400 bytes it will fail!
while (true)
{
var i = ms.Read(sizeOfArray, 0, 2);
if (i < 1)
break;
var sizeOfBuffer = BitConverter.ToUInt16(sizeOfArray);
int position = 0;
while (position < sizeOfBuffer)
position = ms.Read(buffer, position, sizeOfBuffer - position);
//using var test = new RawBsonDocument(buffer);
using var test = new RawBsonDocumentWrapper(buffer , sizeOfBuffer);
var identityBson = test.ToBsonDocument();
var cc = BsonSerializer.Deserialize<Call>(identityBson);
clones.Add(cc);
}
sb.Append("time to deserialize from memory into clones: ");
sb.AppendLine(sw.Elapsed.TotalMilliseconds.ToString());
sw.Restart();
var serializedjs = new List<string>();
foreach(var item in clones)
{
var foo = item.SerializeToJsStandards();
if (foo.Contains("jaja"))
throw new Exception();
serializedjs.Add(foo);
}
sb.Append("time to serialze into js: ");
sb.AppendLine(sw.Elapsed.TotalMilliseconds.ToString());
sw.Restart();
foreach(var item in serializedjs)
{
try
{
var obj = item.DeserializeUsingJsStandards<Call>();
if (obj is null)
throw new Exception();
if (obj.IdAccount.Contains("jsfjklsdfl"))
throw new Exception();
}
catch(Exception ex)
{
Console.WriteLine(ex);
throw;
}
}
sb.Append("time to deserialize js: ");
sb.AppendLine(sw.Elapsed.TotalMilliseconds.ToString());
sw.Restart();

Check if values are those same objects

I'm pretty new to Dart and I got some problems with understanding what is going on under the hood. I've simple code like this:
void main() {
String s1 = "test1";
StringBuffer sb = StringBuffer();
sb.write("test");
sb.write("1");
String s2 = sb.toString();
print(identical(s1, s2));
print(s1 == s2);
int a = 1;
int b = 1;
print(identical(a, b));
}
result of this code is 3x true
Which is little tricky for me. As I understands all variables refers to some values, and since == operator is expected to return true, I'm little confused why identical also return true. If I would use code like:
String s1 = "test1";
String s2 = "test1";
Then I would expect that there is compiler optimization for immutable strings. But since I combine it via StringBuffer I would expect that I got two different values in memory. I suspect that identical uses == operator internally... Why I'm curious: I'm working on app which will use sets of tags. And I'm not sure If should I create some pool of strings and checks if some strings are already created and reuse it or just spawn strings and Dart has it's own string pool?

Concatenating strings with RxJava2

I have a simple List of my POJO called Sport which has a name field .
What is the best way to concat all the names in the List?
Observable.just(member.getSports())
.map(sports -> {
StringBuilder builder = new StringBuilder();
for (Sport sport : sports) {
builder.append(sport.getName());
}
return builder.toString()
})
I believe there must be a better way ?
Willi Mentzel is right that you don't need Rx, but if you want to use it anyway in plain Java for readability, here's a cool way to leverage Rx2
// Get a list of sports names
Observable<String> names = Observable.fromIterable(member.getSports()).map(sport -> sport.getName())
// Concat list into a single string output separated by comma
Single<String> listOfNames = names.collect(StringBuilder::new, (sb, x) -> sb.append(x).append(", ")).map(StringBuilder::toString).toSingle()
One-liner in Java:
Observable.fromIterable(member.getSports()).map(sport -> sport.getName()).collect(StringBuilder::new, (sb, x) -> sb.append(x).append(", ")).map(StringBuilder::toString).toSingle()
One-liner in Kotlin with RxKotlin
member.getSports().map { it.name }.toObservable().collect(StringBuilder::new, (sb, x) -> sb.append(x).append(", ")).map(StringBuilder::toString).toSingle()
Produces:
volleyball, hockey, racquetball
You should always strive for code that is easy to read and therefore to maintain.
You don't need RxJava for that, just streams:
String joined = member.getSports().stream()
.map(sport -> sport.getName())
.collect(Collectors.joining(""));
Edit 1:
Since streams are not an option. I wouldn't use RxJava then anyways. Just use the code from inside your lambda.
StringBuilder builder = new StringBuilder();
for (Sport sport : sports) {
builder.append(sport.getName());
}
builder.toString()
Edit 2:
Or you build a more generalized version, in case you need a prefix and postfix as well:
// Inside Utils.java
public static <T> String joinToString(List<T> list, String separator, String prefix, String postfix, Function<T, String> func) {
StringBuilder builder = new StringBuilder();
builder.append(prefix);
if (!list.isEmpty()) {
for (T t : list.subList(0, list.size() - 1)) {
builder.append(func.apply(t));
builder.append(separator);
}
builder.append(list.get(list.size() - 1));
}
builder.append(postfix);
return builder.toString();
}
and use it like this:
Utils.joinToString(member.getSports(), "", "", "", Sport::getName));
This comes pretty close to Kotlin's joinToString extension function.

rxjava2 - how to zip Maybe that can be empty?

I am attempting to make 3 web services calls (e.g.: getPhoneNumber, getFirstName, getLastName) and collect the answers into a common object Person. Any of the web services calls can return a Maybe.empty().
When attempting to zip the response together, rxjava2 skips over the zip operation and terminate normally (without having aggregated my answer).
For a simplified example, see below:
#Test
public void maybeZipEmptyTest() throws Exception {
Maybe<Integer> a = Maybe.just(1);
Maybe<Integer> b = Maybe.just(2);
Maybe<Integer> empty = Maybe.empty();
TestObserver<String> observer = Maybe.zip(a, b, empty, (x, y, e) -> {
String output = "test: a "+x+" b "+y+" empty "+e;
return output;
})
.doOnSuccess(output -> {
System.out.println(output);
})
.test();
observer.assertNoErrors();
}
How can we collect empty values within a zip operation instead of having the zip operation skipped/ignored? If this is the wrong pattern to solve this problem, how would you recommend solving this?
For most use cases, leveraging the defaultIfEmpty method is the right way to go.
For representing something which is ultimately optional (doesn't even use default), I used Java 8 Optional type to represent.
For example
#Test
public void maybeZipEmptyTest() throws Exception {
Maybe<Optional<Integer>> a = Maybe.just(Optional.of(1));
Maybe<Optional<Integer>> b = Maybe.just(Optional.of(2));
Maybe<Optional<Integer>> empty = Maybe.just(Optional.empty());
TestObserver<String> observer = Maybe.zip(a, b, empty, (x, y, e) -> {
String output = "test: a "+toStringOrEmpty(x)+" b "+toStringOrEmpty(y)+" empty "+toStringOrEmpty(e);
return output;
})
.doOnSuccess(output -> {
System.out.println(output);
})
.test();
observer.assertNoErrors();
}
private String toStringOrEmpty(Optional<Integer> value){
if(value.isPresent()){
return value.get().toString();
}
else {
return "";
}
}

working with Query String in GWT

I have to created a dynamic URLcontaining the user id and email parameters, which will direct to sign up form in my GWT application. I want to set and get the parameters in the query string. I have referred tp http://code.google.com/p/gwt-examples/source/browse/trunk/System/src/com/gawkat/gwt/system/client/global/QueryString.java?r=1241 but here QueryStringData is inaccessible to my project.Please tell me how I can do it? Any alternative could also help me.
#Stein, but there is (a query parameter tokenizer in GWT): e.g. Window.Location.getParameter("debug") will return the string value of the parameter debug.
Don't think there's a simple tokenized query string parser in GWT. But you can get the raw query string by using:
String queryString = Window.Location.getQueryString();
Parse it any way you like. I use it like this to set debug flags etc.:
boolean debugMode = Window.Location.getQueryString().indexOf("debug=true") >= 0;
Note that changing values in the query part of the url (between the ? and the #) will reload the page. While changing the "hash part" of the url (anything after the #) will not reload the page. Which is why the com.google.gwt.user.client.History uses the hash part.
If you want really want to parse the history token (hash part) to encode parameters, here's the code for that:
private static Map<String, String> buildHashParameterMap() {
final String historyToken = History.getToken();
Map<String, String> paramMap = new HashMap<String, String>();
if (historyToken != null && historyToken.length() > 1) {
for (String kvPair : historyToken.split("&")) {
String[] kv = kvPair.split("=", 2);
if (kv.length > 1) {
paramMap.put(kv[0], URL.decodeQueryString(kv[1]));
} else {
paramMap.put(kv[0], "");
}
}
}
return paramMap;
}
There is in-built support for getting all of the parameters.
Simply call:
Map<String, List<String>> parameterMap = Window.Location.getParameterMap();