Beginner question:
How to make an Array of objects from a Struct in Crystal? Or how to make an array of objects in Crystal? I am trying to emulate the go code.
struct Book
def initialize(
#Id : Int32,
#Title : String,
#Author : String,
#Desc : String
)
end
end
books = [] of Int32 | String <--- This is wrong
book1 = Book.new(1, "Hello", "Me", "This is a test.")
GO CODE:
type Book struct {
Id int `json:"id"`
Title string `json:"title"`
Author string `json:"author"`
Desc string `json:"desc"`
}
var Books = []models.Book{
{
Id: 1,
Title: "Golang",
Author: "Gopher",
Desc: "A book for Go",
},
}
Changing to a class allows me to return a new object. Which I am sure is the wrong way to make a new object in Crystal? I can then add objects to the array.
class Book
def initialize(id : Int32, title : String, author : String, desc : String)
#id = id
#title = title
#author = author
#desc = desc
end
def object
{
#id,
#title,
#author,
#desc
}
end
end
books = [] of Book <--- HOW TO SET ARRAY OF OBJECTS ON THE TYPE?
book1 = Book.new(1, "Hello", "Me", "This is a test.")
puts book1.object
books << book1.object
puts books
You can write
books = [] of Book
books << book1
or you can simply do this, and let Crystal recognise the type:
books = [book1]
The closest to the original is
books = [
Book.new(1, "Hello", "Me", "This is a test.")
]
EDIT: If you need JSON, you need to implement it, as described in the documentation. The easiest is to include JSON::Serializable:
require "json"
module TestBookJSON
struct Book
include JSON::Serializable # <-- this
def initialize(
#Id : Int32,
#Title : String,
#Author : String,
#Desc : String
)
end
end
books = [] of Book
book1 = Book.new(1, "Hello", "Me", "This is a test.")
books << book1
puts books.to_json
# => [{"Id":1,"Title":"Hello","Author":"Me","Desc":"This is a test."}]
end
It seems really what I wanted, and what was not the same as the struct version in go was an Array of Hashes, which in turn is an Array of Objects, as everything is an Object in Crystal?*.
hash = [] of Hash(Int32, String) # Array of Hashes
hash0 = {0 => "Jackson0"}
hash1 = {1 => "Jackson1"}
hash2 = {2 => "Jackson2"}
hash3 = {3 => "Jackson3"}
#Append the hashes to the array
hash << hash0
hash << hash1
hash << hash2
hash << hash3
OR
(0..3).each do |i|
hash << {i => "Jackson#{i}"}
end
RESULT : [{0 => "Jackson0"}, {1 => "Jackson1"}, {2 => "Jackson2"}, {3 => "Jackson3"}]
# Display them as JSON
get "/" do
hash.to_json
end
RESULT : [{"0":"Jackson0"},{"1":"Jackson1"},{"2":"Jackson2"},{"3":"Jackson3"}]
Which in turn can be queried using hash[0][1]
RESULT : {"1": "Jackson1"}
Related
Background:
I have some code (Rust) that finds (Regex) matches and assigns the found values to fields in a struct named Article (where all fields are of type String):
pub struct Article {
// user facing data
title: String,
category: String,
subcategory: String,
genre: String,
published: String,
estimated_read_time: String,
description: String,
tags: String,
keywords: String,
image: String,
artwork_credit: String,
// meta data
metas: String,
// location
path: String,
slug: String,
// file data
content: String
}
A regular expression ("//\- define (.*?): (.*?)\n") is used to extract comments from the article's template that define data for that article:
// iterate through HTML property pattern matches
for capture in re_define.captures_iter(&file_content as &str) {
// remove the declaration from the the HTML output
article_content = article_content.replace(&capture[0].to_string(), "");
// get the property value
let property_value: &String = &capture[2].to_string();
// determine what field to assign the property to and assign it
match capture[1].to_lowercase().as_str() {
"title" => article.title = property_value.clone(),
"category" => article.category = property_value.clone(),
"subcategory" => article.subcategory = property_value.clone(),
"genre" => article.genre = property_value.clone(),
"published" => article.published = property_value.clone(),
"estimated_read_time" => article.estimated_read_time = property_value.clone(),
"description" => article.description = property_value.clone(),
"tags" => article.tags = property_value.clone(),
"keywords" => article.keywords = property_value.clone(),
"image" => article.image = property_value.clone(),
unknown_property # _ => {
println!("Ignoring unknown property: {}", &unknown_property);
}
}
}
Note: article is an instance of Article.
Issue:
The code works but what I'm concerned about the following part:
"title" => article.title = property_value.clone(),
"category" => article.category = property_value.clone(),
"subcategory" => article.subcategory = property_value.clone(),
"genre" => article.genre = property_value.clone(),
"published" => article.published = property_value.clone(),
"estimated_read_time" => article.estimated_read_time = property_value.clone(),
"description" => article.description = property_value.clone(),
"tags" => article.tags = property_value.clone(),
"keywords" => article.keywords = property_value.clone(),
"image" => article.image = property_value.clone(),
It calls .clone() on the same String (property_value) for every match (10 matches per article template), for every article template (a couple dozen templates in total), and I don't think it's the most efficient way to do it.
Note: I'm not sure if match is cloning for non-matches.
What I tried:
I tried referencing the property_value String, but I got an error for each reference.
Error from IDE (VS Code):
mismatched types
expected struct `std::string::String`, found `&&std::string::String`
expected due to the type of this binding
try using a conversion method: `(`, `).to_string()`
Error from cargo check:
error[E0308]: mismatched types
--> src/article.rs:84:38
|
84 | "image" => article.image = &property_value,
| ------------- ^^^^^^^^^^^^^^^ expected struct `std::string::String`, found `&&std::string::String`
| |
| expected due to the type of this binding
|
help: try using a conversion method
|
84 | "image" => article.image = (&property_value).to_string(),
| + +++++++++++++
I did try using .to_string(), but I'm not sure if converting a String to the same type is the most efficient to do it either.
Question:
How do I avoid calling .clone() on property_value so many times?
Going by the types, you should just be able to drop the borrow in property_value and then you don't need the .clone()s.
let property_value: &String = &capture[2].to_string();
// change to
let property_value: String = capture[2].to_string();
// or just simply
let property_value = capture[2].to_string();
I'm assuming this was added as capture[2] returns a str (non-sized type) which would require the & but with to_string() it converts to the owned type String which is fine on it's own. This wont have any performance effect as to_string() copies anyway.
it's my first post.
I work to Quasar (Vue.js)
I have list of jobs, and in this list, i have words with special caractere.
Ex :
[ ...{ "libelle": "Agent hôtelier" },{"libelle": "Agent spécialisé / Agente spécialisée des écoles maternelles -ASEM-"},{ "libelle": "Agriculteur / Agricultrice" },{ "libelle": "Aide aux personnes âgées" },{ "libelle": "Aide de cuisine" },...]
And on "input" i would like to search "Agent spécialisé" but i want to write "agent specialise" (without special caractere) or the initial name, i want to write both and autocomplete my "input".
I just don't fin the solution for add to my filter code ...
My input :
<q-select
filled
v-model="model"
use-input
hide-selected
fill-input
input-debounce="0"
:options="options"
hint="Votre métier"
style="width: 250px; padding-bottom: 32px"
#filter="filterFn"
>
</q-select>
</div>
My code :
export default {
props: ['data'],
data() {
return {
jobList: json,
model: '',
options: [],
stringOptions: []
}
},
methods: {
jsonJobsCall(e) {
this.stringOptions = []
json.forEach(res => {
this.stringOptions.push(res.libelle)
})
},
filterFn(val, update) {
if (val === '') {
update(() => {
this.jsonJobsCall(val)
this.options = this.stringOptions
})
return
}
update(() => {
const regex = /é/i
const needle = val.toLowerCase()
this.jsonJobsCall(val)
this.options = this.stringOptions.filter(
v => v.replace(regex, 'e').toLowerCase().indexOf(needle) > -1
)
})
},
}
}
To sum up : i need filter for write with or witouth special caractere in my input for found in my list the job which can contain a special character.
I hope i was clear, ask your questions if i haven't been.
Thanks you very much.
I am not sure if its work for you but you can use regex to create valid filter for your need. For example, when there is "e" letter you want to check "e" or "é" (If I understand correctly)
//Lets say we want to match "Agent spécialisé" with the given search text
let searchText = "Agent spe";
// Lets create a character map for matching characters
let characterMap = {
e: ['e', 'é'],
a: ['a', '#']
}
// Replacing special characters with a regex part which contains all equivelant characters
// !Remember replaceAll depricated
Object.keys(characterMap).forEach((key) => {
let replaceReg = new RegExp(`${key}`, "g")
searchText = searchText.replace(replaceReg, `[${characterMap[key].join("|")}]`);
})
// Here we create a regex to match
let reg = new RegExp(searchText + ".*")
console.log("Agent spécialisé".match(reg) != null);
Another approach could be the reverse of this. You can normalize "Agent spécialisé". (I mean replace all é with normal e with a regex like above) and store in the object along with the original text. But search on this normalized string instead of original.
I am following this article to set the Order for sorting.
Excerpt from above article
class Language {
String name
boolean dynamic
String toString() { "name: $name, dynamic: $dynamic" }
}
def languages = [
new Language(name: 'Groovy', dynamic: true),
new Language(name: 'Java', dynamic: false),
new Language(name: 'Clojure', dynamic: true)
]
def list = ['name', 'dynamic']
sh = new GroovyShell()
closure = sh.evaluate("{ }")
def cList = list.collect { closure(it) }
println cList
// We order first on dynamic property and then name property.
def orderByDynamicAndName = new OrderBy([{ it.dynamic }, { it.name }])
Here, I would like to pass the list of closures dynamically from a list.
Say, there is a list and list may vary in element size in different applications / classes. This is main reason I wanted the dynamic closure list.
def list = ['name', 'dynamic']
From the above list, want to generate it as list of closure and pass it to OrderBy class as argument.
//Build closure list. But, not sure how to generate it from above list
def cList =
def orderByDynamicAndName = new OrderBy(cList)
Tried to refer this thread, but some how could not generate cList as desired
Tried to build cList as shown below; getting errors
def list = ['name', 'dynamic']
sh = new GroovyShell()
closure = sh.evaluate("{ fieldName -> \"it\".fieldName }")
def cList = list.collect { closure(it) }
Error:
Exception thrown
groovy.lang.MissingPropertyException: No such property: fieldName for class: java.lang.String
How to over come this?
class Language {
String name
boolean dynamic
String toString() { "name: $name, dynamic: $dynamic" }
}
def languages = [
new Language(name: 'Groovy', dynamic: true),
new Language(name: 'Java', dynamic: false),
new Language(name: 'Clojure', dynamic: true)
]
def list = ['dynamic', 'name']
def cList = list.collect{ propName-> { target-> target[propName] } }
def orderBy = new OrderBy(cList)
def sortedLanguages = languages.toSorted(orderBy)
println languages
println sortedLanguages
actually this expression
list.collect{ propName-> { target-> target[propName] } }
converts list of property names to list of closures
['dynamic', 'name'] => [ { target-> target['dynamic'] }, { target-> target['name'] } ]
and target is just a parameter name in the closure.
later, when we call sort, each closure { target-> target[propName] } will be called against an object in a sorting array and our closure returns the value by a property name.
After trial and error, below code worked for me in order to create the list of closures.
def list = ['dynamic', 'name']
def tempClosureString = list.collect { element -> "{it.$element}" }.join(',')
def cList = new GroovyShell().evaluate("[ $tempClosureString ]")
def orderByDynamicAndName = new OrderBy(cList)
I welcome if there are better alternatives.
I'm having problem storing lists of embedded documents/objects in MongoDB using the Grails MongoDB plugin. I used the information given in the documentation in chapter 3 but only got the embedding of one object working.
For testing purposes I created two domain objects Person and Address in a new Grails project. They look like this:
class Person {
ObjectId id
String firstName
String lastName
Address address
List otherAddresses = []
static embedded = ['address', 'otherAddresses']
}
class Address {
String street
String postCode
String city
}
When I execute the following lines in Bootstrap.groovy it stores two Person objects in MongoDB - both have a correct address but in person1 the otherAddresses List is "[ null ]" and in person2 the otherAddresses List is "[ { "street" : "Second Street. 164" , "city" : "New York" , "postCode" : "13579"}]"
def address = new Address(street: "Mainstreet. 164", city: "New York", postCode:"12345")
def person1 = new Person(firstName: "John", lastName: "Doe")
person1.address = address
person1.otherAddresses.add(address)
println person1.otherAddresses // Result: "[mongoembeddedlisttest.Address : (unsaved)]"
person1.save()
person1.errors.allErrors.each { println it } // no errors
def person2 = new Person(firstName: "Jane", lastName: "Doe")
person2.otherAddresses += ['street': 'Second Street. 164', 'city': 'New York', 'postCode':'13579']
println person2.otherAddresses // Result: "[[street:Second Street. 164, city:New York, postCode:13579]]"
person2.save()
Resulting Database Entries:
{ "_id" : { "$oid" : "521089461a150b20390d61c2"} , "address" : { "city" : "New York" , "postCode" : "12345" , "street" : "Mainstreet. 164"} , "firstName" : "John" , "lastName" : "Doe" , "otherAddresses" : [ null ] , "version" : 0}
{ "_id" : { "$oid" : "521089461a150b20390d61c3"} , "firstName" : "Jane" , "lastName" : "Doe" , "otherAddresses" : [ { "street" : "Second Street. 164" , "city" : "New York" , "postCode" : "13579"}] , "version" : 0}
Further Notes:
I'm using a pure mongodb approach (no a hybrid together with Hibernate)
I'm working on a Windows 8 machine using Grails 2.2.1 running mongo db 2.4.4
Person is a domain object in /grails-app/domain and Address is a "normal" groovy class in /src/groovy (I can put it in domain folder but that has no effect)
Everything is set to be nullable in Config.groovy: grails.gorm.default.constraints = { '*'(nullable: true) }
BuildConfig.groovy has the plugin entry: compile ":mongodb:1.3.0"
What am I doing wrong? How can I store a list of embedded objects using the Grails mechanism?
I think you're adding a map in the second person instead of an Address object. Is there any reason why you're adding the otherAddress different for each person?
I think this should work although I haven't tested it:
def address = new Address(street: "Mainstreet. 164", city: "New York", postCode:"12345")
def person1 = new Person(firstName: "John", lastName: "Doe")
person1.address = address
person1.otherAddresses.add(address)
println person1.otherAddresses // Result: "[mongoembeddedlisttest.Address : (unsaved)]"
person1.save()
person1.errors.allErrors.each { println it } // no errors
def person2 = new Person(firstName: "Jane", lastName: "Doe")
person2.otherAddresses += new Address('street': 'Second Street. 164', 'city': 'New York', 'postCode':'13579')
println person2.otherAddresses
person2.save()
I want to create a map of members, but every membres have 3 propreties : first name, last name, and username. How can I create like a list of liste, but with a map.
So I want to have something like :
var membres= {['lastname': 'Bonneau',
'firstname': 'Pierre',
'username': 'mariobross'],
['lastname': 'Hamel',
'firstname': 'Alex',
'username': 'Queenlatifa'],
};
As you know, this code doesn't work. But it explain pretty well what I am trying to do.
I think you are confusing the two constructs here.
Read this introduction to the language: http://www.dartlang.org/docs/dart-up-and-running/ch02.html#lists
A list is a list of elements which can be denoted with the shorthand [...] syntax:
var list = [1, 2, "foo", 3, new Date.now(), 4];
Whereas a map can be denoted with the curly brace shorthand syntax:
var gifts = { // A map literal
// Keys Values
'first' : 'partridge',
'second' : 'turtledoves',
'fifth' : 'golden rings'
};
So, let's modify your code to work:
var members = [
{
'lastname': 'Bonneau',
'firstname': 'Pierre',
'username': 'mariobross'
},
{
'lastname': 'Hamel',
'firstname': 'Alex',
'username': 'Queenlatifa'
}
];
You can, for example, print the information like this:
members.forEach((e) {
print(e['firstname']);
});
If I understand your intent correctly, you want to have a list of maps. What you have is correct except you confused [ and {. The following works:
var membres = [
{'lastname': 'Bonneau',
'firstname': 'Pierre',
'username': 'mariobross'},
{'lastname': 'Hamel',
'firstname': 'Alex',
'username': 'Queenlatifa'}
];
As an example, to get a list of all usernames:
print(membres.map((v) => v['username']));
If you don't really need a Map, what about using a class to improve the structure of your code :
class Member {
String firstname;
String lastname;
String username;
Member(this.firstname, this.lastname, this.username);
}
main() {
final members = new List<Member>();
members.add(new Member('Pierre', 'Bonneau', 'mariobross'));
members.add(new Member('Alex', 'Hamel', 'Queenlatifa'));
// use members
}
You mean like this?
// FirstName => LastName => Value
var lookup = new Map<String, Map<String, String>>();
// get / set values like this
void setValue(String firstName, String lastName, String value) {
if (!lookUp.containsKey(firstName))
lookUp[firstName] = new Map<String, String>();
lookUp[firstName][lastName] = value;
}
String getValue(String firstName, String lastName) {
if (!lookUp.containsKey(firstName)) return "";
return lookUp[firstName][lastName];
}
First of all you need to create a map with value as list. Dont forget to initialize it
then if you want to fill it you first need to use built in function like putIfAbsent as in dart to add first object in list and then use update to add items in list. therefore you will need two arrays. First to put elements and then to add elements in list with same key. Also you can use try catch to identify if the key is present or not to do that in one loop
for (var item in days) {
var date_time = DateTime.parse(item["date"] + " 00:00:00");
_events[date_time] = _events.putIfAbsent(
date_time,
() => [
{
"title": item["title"],
"date": item["date"],
"time": reUse.get_time_am_pm_format(item["time"]),
"feature": item["feature"],
}
]);
}
for (var item in days) {
var date_time = DateTime.parse(item["date"] + " 00:00:00");
_events[date_time] = _events.update(date_time, (value) {
value.add({
"title": item["title"],
"date": item["date"],
"time": reUse.get_time_am_pm_format(item["time"]),
"feature": item["feature"],
});
return value;
});
}