Array typing issue in build macro - macros

Note: My issue #4417 was closed, but I didn't want to be that guy who opens another issue for the same thing.
Based on #3132, [ { "a": 1, "b": 2 }, { "a": 2 } ] doesn't compile unless you specifically type it to Array<Dynamic> or whatever type encompasses both. That's fine I guess, but inside of the build macro below, there is nowhere for me to type the array, and I get an error.
In general, I can make map literal notation work using untyped (http://try.haxe.org/#3dBf5), but I can't do that here since my types haven't been constructed yet.
macro public static function test():Array<Field> {
var fields = Context.getBuildFields();
// parse the JSON
var o = Context.parseInlineString('{ "arr": [ { "a": 1, "b": 2 }, { "a": 2 } ] }', Context.currentPos());
// ["test" => json] map literal notation
var a = [{ expr : EBinop(OpArrow, macro $v { "test" }, o), pos : Context.currentPos() }];
// creates: "public var json:StringMap<Dynamic> = ['test' => json];"
var nf:Field = {
name : "json",
doc : "docs",
meta : [],
access : [APublic],
kind : FVar(macro : haxe.ds.StringMap<Dynamic>, { expr : EArrayDecl(a), pos : Context.currentPos() } ),
pos : Context.currentPos()
};
fields.push(nf);
return fields;
// error: Arrays of mixed types...
}
Without knowing ahead of time what the structure of the json is, is there anything I can do?

You can still use untyped, by constructing an intermediate EUntyped(o) expression (more simply macro untyped $o).
Alternatively, you can traverse the parsed object and add ECheckType to Dynamic expressions to every array, generating something like to ([...]:Array<Dynamic>).
The implementation of this would look something like calling the following checkTypeArrays function with your parsed o object, before building the map literal expression.
static function checkTypeArrays(e:Expr):Expr
{
return switch (e) {
case { expr : EArrayDecl(vs), pos : pos }:
macro ($a{vs.map(checkTypeArrays)}:Array<Dynamic>);
case _:
haxe.macro.ExprTools.map(e, checkTypeArrays);
}
}
An improvement to this would be to only wrap in (:Array<Dynamic>) the arrays that fail Context.typeof(expr).

Related

How do you create a Swift sequence based on hierarchical data?

Routinely in my various projects, I have to deal with iterating over hierarchical data. Being as common as it is, it always frustrated me that I had to write so much boilerplate code to do it.
Well thanks to Swifts ability to write custom Sequence classes, I decided to see if I could write one that would achieve this goal in a reusable fashion. Below is my result.
I decided to post this here per Jeff Atwood's [own comments on encouraging posting your own answers][1] where he says...
It is not merely OK to ask and answer your own question, it is explicitly encouraged [...] I do it all the time!
As such, I'm providing this solution here in hopes of helping others when they come to search this site.
Enjoy! :)
As stated above, I wrote a class that allows you to iterate over a hierarchical set of data, while keeping that hierarchy in order. You do this by specifying one or more root elements (either via an array or a variadic), and a closure that returns the children for a given element.
Since it's implemented as a generic, you can specify an explicit type to use if you know the hierarchy is homogenous, but if not, specify Any for the type, then in the closure, perform the logic to determine what child type it is.
In addition, the implementation, via recursion, not only returns things in the correct hierarchical order, but it also returns a level so you know how deep the items are. If you don't care about the level, simply append .map{ $0.item } when initializing the sequence to extract the items directly.
Here's the code for the custom hierarchical sequence...
struct HierarchicalSequence<TItem> : Sequence {
typealias GetChildItemsDelegate = (TItem) -> [TItem]?
init(_ rootItems:TItem..., getChildItems: #escaping GetChildItemsDelegate){
self.init(rootItems, getChildItems: getChildItems)
}
init(rootItems:[TItem], getChildItems: #escaping GetChildItemsDelegate){
self.rootItems = rootItems
self.getChildItems = getChildItems
}
let rootItems : [TItem]
let getChildItems : GetChildItemsDelegate
class Iterator : IteratorProtocol {
typealias Element = (level:Int, item:TItem)
init(level:Int, items:[TItem], getChildItems: #escaping GetChildItemsDelegate){
self.level = level
self.items = items
self.getChildItems = getChildItems
}
let level : Int
let items : [TItem]
let getChildItems : GetChildItemsDelegate
private var nextIndex = 0
var childIterator:Iterator?
func next() -> Element? {
// If there's a child iterator, use it to see if there's a 'next' item
if let childIterator = childIterator {
if let childIteratorResult = childIterator.next(){
return childIteratorResult
}
// No more children so let's clear out the iterator
self.childIterator = nil
}
if nextIndex == items.count {
return nil
}
let item = items[nextIndex]
nextIndex += 1
// Set up the child iterator for the next call to 'next' but still return 'item' from this call
if let childItems = getChildItems(item),
childItems.count > 0 {
childIterator = Iterator(
level : level + 1,
items : childItems,
getChildItems : getChildItems)
}
return (level, item)
}
}
func makeIterator() -> Iterator {
return Iterator(level: 0, items: rootItems, getChildItems: getChildItems)
}
}
Let's see an example of how to use it. First, let's start with some JSON data...
public let jsonString = """
[
{
"name" : "Section A",
"subCategories" : [
{
"name" : "Category A1",
"subCategories" : [
{ "name" : "Component A1a" },
{ "name" : "Component A1b" }
]
},
{
"name" : "Category A2",
"subCategories" : [
{ "name" : "Component A2a" },
{ "name" : "Component A2b" }
]
}
]
},
{
"name" : "Section B",
"subCategories" : [
{
"name" : "Category B1",
"subCategories" : [
{ "name" : "Component B1a" },
{ "name" : "Component B1b" }
]
},
{
"name" : "Category B2",
"subCategories" : [
{ "name" : "Component B2a" },
{ "name" : "Component B2b" }
]
}
]
}
]
"""
Here's the models and code to load that data
class Category : Codable {
let name : String
let subCategories : [Category]?
}
public let jsonData = jsonString.data(using: .utf8)!
var rootCategories = try! JSONDecoder().decode([Category].self, from: jsonData)
Here's how you use the sequence getting all the categories along with their depths...
let allCategoriesWithDepth = HierarchicalSequence(rootItems:rootCategories){ $0.subCategories }
for (depth, category) in allCategoriesWithDepth {
print("\(String(repeating: " ", count: depth * 2))\(depth): \(category.name)")
}
And finally, here's the output...
0: Section A
1: Category A1
2: Component A1a
2: Component A1b
1: Category A2
2: Component A2a
2: Component A2b
0: Section B
1: Category B1
2: Component B1a
2: Component B1b
1: Category B2
2: Component B2a
2: Component B2b
Enjoy!

How to retrieve []bson.M type of map

How to retrieve multidimensional []bson.M type of map
The data in mongo is like
"taskData" : {
"createdOn" : ISODate("2016-02-20T21:23:11.903Z"),
"Task_content" : "#bob",
"Priority" : "2",
"owner_Uname" : "alice"
}
The code through which i tried to access it
var n []bson.M
e := collection.Find(bson.M{"users."+strconv.Itoa(j)+".user_name" : r.FormValue("value[userName]")}).Select(bson.M{"taskData.owner_Uname":1,"_id":0}).All(&n)
if e != nil {
fmt.Println("Error : ",e)
}else{
fmt.Println(n[0]["taskData"])
}
getting output like this
map[owner_Uname:alice]
I need to access this resultant string with another query.
It is a interface i tried to convert it to simple map
newMap :=n[0]["taskData"].(map[string]interface{})but it gives me an runtime error interface conversion: interface {} is bson.M, not map[string]interface {}
result := rawData{}
err := collection.Find(bson.M{"user_name":n[0]["taskData"]["owner_Uname"]}).All(&result)
Now I want to use it in above query ...
Kindly help me out. Thanks in advance
Edit :-
The data in mongo is like
{
"_id" : ObjectId("56bf128f5a9a6a0ebfdd5075"),
"deadLine" : {
"Start_time" : ISODate("2016-05-24T00:00:00Z"),
"End_time" : ISODate("2016-05-29T00:00:00Z")
},
},
"taskData" : {
"createdOn" : ISODate("2016-02-20T21:23:11.903Z"),
"Task_content" : "#bob",
"Priority" : "2",
"owner_Uname" : "alice"
},
"group" : {
"1" : {
"grp_name" : "grp"
},
"2" : {
"grp_name" : "secondGrp"
}
}
That will work me too if it is done with nested struct or map in struct
I'll provide you with a general example to help you understand, since SO is not a free coding service, but a platform where peers help each other to take a grasp on the problem.
My approach is not to use bson.M at all for the returned value.
package main
import (
"fmt"
"time"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
type Baz struct {
Date time.Time
Value int
}
type Bar struct {
Name string
Baz []Baz
}
type Foo struct {
Owner string
hidden int
Bar Bar
}
const (
ds = "localhost:27017"
db = "test"
coll = "nestdemo"
)
func main() {
o := Foo{
Owner: "me",
hidden: 1,
Bar: Bar{
Name: "funky",
Baz: []Baz{
Baz{Date: time.Now(), Value: 42},
},
},
}
// CHECK ERRORS in production environments
conn, _ := mgo.Dial(ds)
defer conn.Close()
c := conn.DB(db).C(coll)
c.Insert(o)
l := &Foo{}
c.Find(bson.M{"owner": "me"}).One(l)
fmt.Printf("Loaded data: %+v\n", l)
fmt.Printf(
"You got your answer to life, the universe and all the rest at %s: %d\n",
l.Bar.Baz[0].Date.Format(time.Kitchen), l.Bar.Baz[0].Value,
)
}
You can run this program on you local machine (with the constants adjusted as needed), which should give you an output looking like this:
$ go run main.go
Loaded data: &{Owner:me hidden:0 Bar:{Name:funky Baz:[{Date:2016-02-24 09:00:06.471 +0100 CET Value:42}]}}
You got your answer to life, the universe and all the rest at 9:00AM: 42
The entry in the according collection should read something like this:
{
"_id" : ObjectId("56cd6306538ba56563bdab76"),
"owner" : "me",
"bar" : {
"name" : "funky",
"baz" : [
{
"date" : ISODate("2016-02-24T08:00:06.471Z"),
"value" : 42
}
]
}
}
A few things are to note here.
I didn't need a single character in my struct definitions to have the structs marshaled to and marshaled from BSON. It was done automagically by mgo according to the rules described in the docs. However, you can customize the behavior of the (un-)marshaling, as described there.
Unexported fields (hidden in this example) take their zero value upon unmarshalling – keep that in mind, it can bite you in the neck.
There is no need to use bson.M to handle your data, which makes life a lot easier – no manual type conversions, for example.
In summary: All you need to do is to create a struct your data can be unmarhaled into. Then you can access the various fields as usual, without string fiddling and alike. That's bit of work, but a rather trivial one, as you can see.
Note: The data model you have shown is both syntactically and conceptually incorrect. Setting aside the former for now: It is a very bad practice to have values as keys, as shown in the group subdoc. This will always force you to deal with string parsing back and forth, making your life with MongoDB as complicated as it can get as a developer.
My above suggestion assumes you will correct that into something like:
{
…
groups:[
{grp_id: 1, grp_name: "grp"},
{grp_id: 2, grp_name: "secondGrp"}
]
…
}

Elastic4s - finding multiple exact values for one term

I'm trying to filter a term to be matching one of the values in an array.
relaying on the ES https://www.elastic.co/guide/en/elasticsearch/guide/current/_finding_multiple_exact_values.html
GET /my_store/products/_search
{
"query" : {
"filtered" : {
"filter" : {
"terms" : {
"price" : [20, 30]
}
}
}
}
}
I tried this:
val res = ESclient.execute {
search in "index" query {
filteredQuery query {
matchall
} filter {
termsFilter("category", Array(1,2))
}
}
But got an error from ES.
How can I do that?
When calling termsFilter, the method is expecting a var args invocation of Any*, so termsFilter("category", 1, 2) would work. But termsFilter("category", Array(1,2)) is treated as a single argument, since Array is a subclass of Any of course. By adding : _ * we force scala to see it as a vars arg invocation.
So this will work:
val res = ESclient.execute {
search in "index" query {
filteredQuery query {
matchall
} filter {
termsFilter("category", Array(1,2) : _ *)
}
}
Maybe the best solution of all is to update the client to be overloaded on Iterables.

What is the best way to iterate over a Hash in KRL?

Let's say I have a hash where I don't know the contents of the hash (so I can't use pick for this). Here is an example of the hash:
{ "key1" : "value1", "key2" : "value2", "key3" : "value3" }
I want to iterate over this hash and create the following array:
["key1=value1", "key2=value2", "key3=value3"]
My first approach would be to build a recursive function that iterates through the hash and populates the array, but I'm not sure if that can be done. In an array, I can use head() and tail() to help with the recursion, but those operators aren't available for a hash (as far as I know).
I want to initiate this from within a function because I'm doing this in a module. For example:
hash_to_array = function(h) {
// magic code here
}
manipulate_params = function(params) {
params_array = hash_to_array(params);
// more code here...
}
Mike, after impact, I will carve out time to build a keys() operator for hashes.
In the meantime, what I have done to get around this is to keep a separate array of the keys. That way, I can use map, filter and all of the set operations on the index and then use the those values as my keys for the hash operations
key_array = ["key1","key2","key3"];
my_hash = { "key1" : "value1", "key2" : "value2", "key3" : "value3" };
This really only works if you are controlling the values in the hash, but here is some example code:
global {
kvHash = { "key1" : "value1", "key2" : "value2", "key3" : "value3" };
kArray = ["key1","key2","key3"];
}
pre {
pickKey = kArray[1];
value = kvHash.pick("$.#{pickKey}");
// add a new value
newKey = "key4";
newVal = "value4";
newArray = kArray.union(newKey);
newHash = kvHash.put([newKey],newVal);
}
Noticed that I used the set operator union to keep the array full of unique values
The javascript that is generated shows what this does:
var pickKey = 'key2';
var value = 'value2';
var newKey = 'key4';
var newVal = 'value4';
var newArray = ['key1', 'key2', 'key3', 'key4'];
var newHash = {'key2' :'value2','key1' :'value1','key4' :'value4','key3' :'value3'};
Now, you can use the map or filter operators to pass each value individually to a function
c.map(function(x){x+2})
c.filter(function(x){x<5})
I'll suggest this:
foreach my_hash setting(key, value)
pre {
my_array.push("#{key}=#{value}");
}
See http://docs.kynetx.com/docs/Select
I believe I've figured this out, but the answer is a little bit of a hack.
hash_to_sorted_array = function(params, names, new_a) {
n = names.head();
val = params.pick("$.#{n}", true);
appended_array = new_a.append("#{n}=#{val.head()}");
finished_array = (names.length() == 0) => new_a |
hash_to_sorted_array(params, names.tail(), appended_array);
finished_array.sort()
}
This recursive function iterates over the names array, which contains the keys in the hash and removes the key that it processes in each iteration.
To call this function, just call:
sorted_array = hash_to_sorted_array(params, names, []);
For those unfamiliar with the head() and tail() methods of an array: head() gives you the first element in the array while tail() gives you a new array with the first element removed.
I don't really love this solution because you have to pass the names, or the keys, of the hash into the function as one of the arguments. As of this writing, I don't know of any way to extract just the keys from a hash, or I would just use that.

How is "foo" different from foo, in a mongodb key:value pair?

When I see a field:value pair as
"name":"foo" and "name":foo
what is the difference between the two? Are both the values supposed to be strings?
And what about
"age":3 and "age":"3"
Is the first one an integer? I am confused.
Thanks.
Strings vs. variables
The following assigns the string value "foo" to a property:
item = { "name" : "foo" } // item.name = "foo"
The following assigns the value of the foo variable to a property. If the foo variable doesn't exist, you'll receive an error message:
item = { "name" : foo } // foo doesn't exist yet, will result in error
foo = "my value" // foo is defined here
item = { "name" : foo } // item.name = "my value"
Numbers vs. strings
The following assigns a Number value to a property:
child = { "age" : 3 } // child.age = 3
Numbers can be used in all mathematical operations. For example, child.age * 3 will result in 9 and child.age + 4 will result in 7.
The following assigns a string value to a property:
child = { "age" : "3" } // child.age = "3"
Strings cannot be used in all calculations. For example, child.age * 3 will result in 9, but child.age + 4 will result in 34, due to string concatenation.
You should only use strings if the data is textual data. If the data is numerical, store it as a number (without the quotes).