Splitting a list into n other lists, and other questions - ceylon

I haven't quite verified the correctness and lack of off-by-one bugs, but bear with me for a moment.
The goal here is to deal/split a deck of cards (defined as a {Card*}) into multiple Decks (which optionally take a {Card*} as a constructor argument). I want to split the cards in a round-robin fashion, like cards would actually be dealt. Here's the code I have so far:
{Deck*} split(Integer ways) {
assert(theCards.size % ways == 0);
{Card*} empty = {};
value decks = LinkedList { for (i in 0:ways) empty };
for(i -> card in entries(theCards)) {
value deckIndex = i % ways;
assert (exists current = decks[deckIndex]);
decks.set(deckIndex, {card, *current});
}
return { for(cards in decks) Deck(cards) };
}
Is this the correct/idiomatic way to split a list into multiple lists?
If I wanted to not reverse all the cards (that is, append to the list instead of prepend, or reverse the iterable) how might I do that?
How would I initialize the values of the decks variable lazily inside the loop?
Is there any way I can get away from needing the empty variable I have?
Any chance I could write this without the need for mutable data structures?
Thanks, and sorry for the multi-question (I'd have created this on codereview.stackexchange.com but I don't have the rep to create a ceylon tag there).

Not reversed, lazy, no mutable data structures:
alias Card => String;
{{Card*}*} deal({Card*} cards, Integer players)
=> {for (i in 0:players) cards.skipping(i).by(players)};
void run() {
value suits = {"♦", "♣", "♥", "♠"};
value ranks = {for (i in 2..10) i.string}.chain{"J", "Q", "K", "A"};
value deck = {for (suit in suits) for (rank in ranks) rank + suit};
print(deal(deck.taking(10), 2)); // { { 2♦, 4♦, 6♦, 8♦, 10♦ }, { 3♦, 5♦, 7♦, 9♦, J♦ } }
}
The laziness and immutable style comes at the cost of iterating through all of the cards for each hand. I prefer this eager solution:
{Card[]*} deal({Card*} cards, Integer players) {
value hands = [for (i in 0:players) SequenceBuilder<Card>()];
for (card->hand in zipEntries(cards, hands.cycled)) {
hand.append(card);
}
return {for (hand in hands) hand.sequence};
}
That way, you're just iterating the deck once.
Note that Ceylon's enumerated types provide a nifty way to represent things like suits and ranks in a type-safe and object-oriented way, analogously to Java's enums:
abstract class Suit(shared actual String string)
of diamonds | clubs | hearts | spades {}
object diamonds extends Suit("♦") {}
object clubs extends Suit("♣") {}
object hearts extends Suit("♥") {}
object spades extends Suit("♠") {}

How about something like:
alias Deck => {Card*};
{Deck*} split(Deck cards, Integer ways) {
assert(ways.divides(cards.size));
return { for i in (0:ways) { for (j->card in entries(cards)) if (j%ways==i) card } };
}
If Deck is actually a class, not very clear from your description, then this:
class Deck(shared {Card*} cards) { ... }
{Deck*} split(Deck deck, Integer ways) {
assert(ways.divides(deck.cards.size));
return { for i in (0:ways) Deck { for (j->card in entries(deck.cards)) if (j%ways==i) card } };
}
Note: I have not tested this.

Related

How to query multiple fields with one value in Firebase Realtime Database? [duplicate]

{
"movies": {
"movie1": {
"genre": "comedy",
"name": "As good as it gets",
"lead": "Jack Nicholson"
},
"movie2": {
"genre": "Horror",
"name": "The Shining",
"lead": "Jack Nicholson"
},
"movie3": {
"genre": "comedy",
"name": "The Mask",
"lead": "Jim Carrey"
}
}
}
I am a Firebase newbie. How can I retrieve a result from the data above where genre = 'comedy' AND lead = 'Jack Nicholson'?
What options do I have?
Using Firebase's Query API, you might be tempted to try this:
// !!! THIS WILL NOT WORK !!!
ref
.orderBy('genre')
.startAt('comedy').endAt('comedy')
.orderBy('lead') // !!! THIS LINE WILL RAISE AN ERROR !!!
.startAt('Jack Nicholson').endAt('Jack Nicholson')
.on('value', function(snapshot) {
console.log(snapshot.val());
});
But as #RobDiMarco from Firebase says in the comments:
multiple orderBy() calls will throw an error
So my code above will not work.
I know of three approaches that will work.
1. filter most on the server, do the rest on the client
What you can do is execute one orderBy().startAt()./endAt() on the server, pull down the remaining data and filter that in JavaScript code on your client.
ref
.orderBy('genre')
.equalTo('comedy')
.on('child_added', function(snapshot) {
var movie = snapshot.val();
if (movie.lead == 'Jack Nicholson') {
console.log(movie);
}
});
2. add a property that combines the values that you want to filter on
If that isn't good enough, you should consider modifying/expanding your data to allow your use-case. For example: you could stuff genre+lead into a single property that you just use for this filter.
"movie1": {
"genre": "comedy",
"name": "As good as it gets",
"lead": "Jack Nicholson",
"genre_lead": "comedy_Jack Nicholson"
}, //...
You're essentially building your own multi-column index that way and can query it with:
ref
.orderBy('genre_lead')
.equalTo('comedy_Jack Nicholson')
.on('child_added', function(snapshot) {
var movie = snapshot.val();
console.log(movie);
});
David East has written a library called QueryBase that helps with generating such properties.
You could even do relative/range queries, let's say that you want to allow querying movies by category and year. You'd use this data structure:
"movie1": {
"genre": "comedy",
"name": "As good as it gets",
"lead": "Jack Nicholson",
"genre_year": "comedy_1997"
}, //...
And then query for comedies of the 90s with:
ref
.orderBy('genre_year')
.startAt('comedy_1990')
.endAt('comedy_2000')
.on('child_added', function(snapshot) {
var movie = snapshot.val();
console.log(movie);
});
If you need to filter on more than just the year, make sure to add the other date parts in descending order, e.g. "comedy_1997-12-25". This way the lexicographical ordering that Firebase does on string values will be the same as the chronological ordering.
This combining of values in a property can work with more than two values, but you can only do a range filter on the last value in the composite property.
A very special variant of this is implemented by the GeoFire library for Firebase. This library combines the latitude and longitude of a location into a so-called Geohash, which can then be used to do realtime range queries on Firebase.
3. create a custom index programmatically
Yet another alternative is to do what we've all done before this new Query API was added: create an index in a different node:
"movies"
// the same structure you have today
"by_genre"
"comedy"
"by_lead"
"Jack Nicholson"
"movie1"
"Jim Carrey"
"movie3"
"Horror"
"by_lead"
"Jack Nicholson"
"movie2"
There are probably more approaches. For example, this answer highlights an alternative tree-shaped custom index: https://stackoverflow.com/a/34105063
If none of these options work for you, but you still want to store your data in Firebase, you can also consider using its Cloud Firestore database.
Cloud Firestore can handle multiple equality filters in a single query, but only one range filter. Under the hood it essentially uses the same query model, but it's like it auto-generates the composite properties for you. See Firestore's documentation on compound queries.
I've written a personal library that allows you to order by multiple values, with all the ordering done on the server.
Meet Querybase!
Querybase takes in a Firebase Database Reference and an array of fields you wish to index on. When you create new records it will automatically handle the generation of keys that allow for multiple querying. The caveat is that it only supports straight equivalence (no less than or greater than).
const databaseRef = firebase.database().ref().child('people');
const querybaseRef = querybase.ref(databaseRef, ['name', 'age', 'location']);
// Automatically handles composite keys
querybaseRef.push({
name: 'David',
age: 27,
location: 'SF'
});
// Find records by multiple fields
// returns a Firebase Database ref
const queriedDbRef = querybaseRef
.where({
name: 'David',
age: 27
});
// Listen for realtime updates
queriedDbRef.on('value', snap => console.log(snap));
var ref = new Firebase('https://your.firebaseio.com/');
Query query = ref.orderByChild('genre').equalTo('comedy');
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot movieSnapshot : dataSnapshot.getChildren()) {
Movie movie = dataSnapshot.getValue(Movie.class);
if (movie.getLead().equals('Jack Nicholson')) {
console.log(movieSnapshot.getKey());
}
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
Frank's answer is good but Firestore introduced array-contains recently that makes it easier to do AND queries.
You can create a filters field to add you filters. You can add as many values as you need. For example to filter by comedy and Jack Nicholson you can add the value comedy_Jack Nicholson but if you also you want to by comedy and 2014 you can add the value comedy_2014 without creating more fields.
{
"movies": {
"movie1": {
"genre": "comedy",
"name": "As good as it gets",
"lead": "Jack Nicholson",
"year": 2014,
"filters": [
"comedy_Jack Nicholson",
"comedy_2014"
]
}
}
}
For Cloud Firestore
https://firebase.google.com/docs/firestore/query-data/queries#compound_queries
Compound queries
You can chain multiple equality operators (== or array-contains) methods to create more specific queries (logical AND). However, you must create a composite index to combine equality operators with the inequality operators, <, <=, >, and !=.
citiesRef.where('state', '==', 'CO').where('name', '==', 'Denver');
citiesRef.where('state', '==', 'CA').where('population', '<', 1000000);
You can perform range (<, <=, >, >=) or not equals (!=) comparisons only on a single field, and you can include at most one array-contains or array-contains-any clause in a compound query:
Firebase doesn't allow querying with multiple conditions.
However, I did find a way around for this:
We need to download the initial filtered data from the database and store it in an array list.
Query query = databaseReference.orderByChild("genre").equalTo("comedy");
databaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
ArrayList<Movie> movies = new ArrayList<>();
for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {
String lead = dataSnapshot1.child("lead").getValue(String.class);
String genre = dataSnapshot1.child("genre").getValue(String.class);
movie = new Movie(lead, genre);
movies.add(movie);
}
filterResults(movies, "Jack Nicholson");
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Once we obtain the initial filtered data from the database, we need to do further filter in our backend.
public void filterResults(final List<Movie> list, final String genre) {
List<Movie> movies = new ArrayList<>();
movies = list.stream().filter(o -> o.getLead().equals(genre)).collect(Collectors.toList());
System.out.println(movies);
employees.forEach(movie -> System.out.println(movie.getFirstName()));
}
The data from firebase realtime database is as _InternalLinkedHashMap<dynamic, dynamic>.
You can also just convert this it to your map and query very easily.
For example, I have a chat app and I use realtime database to store the uid of the user and the bool value whether the user is online or not. As the picture below.
Now, I have a class RealtimeDatabase and a static method getAllUsersOnineStatus().
static getOnilineUsersUID() {
var dbRef = FirebaseDatabase.instance;
DatabaseReference reference = dbRef.reference().child("Online");
reference.once().then((value) {
Map<String, bool> map = Map<String, bool>.from(value.value);
List users = [];
map.forEach((key, value) {
if (value) {
users.add(key);
}
});
print(users);
});
}
It will print [NOraDTGaQSZbIEszidCujw1AEym2]
I am new to flutter If you know more please update the answer.
ref.orderByChild("lead").startAt("Jack Nicholson").endAt("Jack Nicholson").listner....
This will work.

CFML/JS Creating nested JSON/Array from plain SQL

I would like to build a tree structre from a plain json array.
The regular depth is approx. 6/7 (max 10) and has about 5,000 records.
My input json looks like this
[3,"01","GruppenAnfangHook",1,0,1,0,"Installationsmaterial",1.0,"",null,null,0.0,-1.0,null,803.0300,803.0300,0.00000,1,1]
[5,"01.001","JumboAnfangHook",3,0,3,0,"MBS Wandler 1.000",6.0,"St",null,null,0.0,-6.0,0.0000,336.7800,56.1300,0.00000,2,2],
[38,"","ArtikelHook",3,5,3,0,"ASK 61.4 1000/5A 5VA Kl.1 Preis lt. Hr. K am 16.05.17",6.0,"stk",6.0,6.0,0.0,-6.0,null,21.5000,21.5000,0.00000,3,3]
But I need it structured with childrens like that
{"0":34,1":"02.003",2":"JumboBegin","3":26,"4":0, "5":26,"6":0, "children":[
{ "0":36,"1":"", "2":"Article","3":26,"4":34,"5":26,6:"0", 7: "Artikel"},
{ "0":35,"1":"", "2":"JumboEnd",3":26,"4":34, "5":26, 6:"0",7:"Stunde"}
]}
My best approach so far was to build the child-structure with the following JS function in the frontend
function nest(data, parentId = 0) {
return data.reduce((r, e) => {
let obj = Object.assign({}, e)
if (parentId == e[4]) {
let children = nest(data, e[0])
if (children.length) obj.children = children
r.push(obj)
}
return r;
}, [])}
It works well and fast (< 1s) with a small (<500) amount of records but my browser begins to freeze at 2,000 and above.
My thought was it is too much data and so I tried to solve it in the CFML backend.
Due to I'm new with recursion, Ben Nadels Blog helped me alot, so I used his post about recursion and created a working example with sample data.
q = queryNew("id,grpCol,jumCol,leiCol,name,typ,order");
The grpCol is level 0, up to 5 groups can be placed in each other, in those groups can be placed two kinds of containers (jumCol and leiCol), they can be placed in each other to, but not in themselfs.
But now I am failing to convert it to a array of structures with child members. The structure of the HTML tree generated as output in the example is exactly what I want for my frontend JSON.
Because of the recursion I don't get, how to store it in an array outside of the function.
My goal is a final return as serzializeJson(array).

Select/isolate in multimodel approach

In API reference described methods to select/isolate objects (in condition that only one model is loaded in viewer):
- select(dbids,selectionType)
- isolate(node)/isolateById(dbids) // that is the difference?
I know select analog for multimodel:
viewer.impl.selector.setSelection([objectIds], model);
Questions are:
Is isolate analog for multimodel mode exists?
How can I select/isolate two objects from diffrenent models at once?
In the recent version of the API the viewer.impl.visibilityManager is returning a MultiModelVisibilityManager, so you can pass a model as second argument:
MultiModelVisibilityManager.prototype.isolate = function (node, model)
Take a look in viewer3D.js (L#17825) to see available methods on that object.
As far as I know there is no way to select two objects from different models in a single call, you would just issue one select call for each model passing respective ids. I don't see a problem with that.
Hope that helps.
For the isolate, you can do something like this(borrowed from the Viewer3D.js):
// Get selected elements from each loaded models
var selection = this.viewer.getAggregateSelection();
var allModels = this.viewer.impl.modelQueue().getModels().concat(); // shallow copy
// Isolate selected nodes.
selection.forEach(function(singleRes){
singleRes.model.visibilityManager.isolate(singleRes.selection);
var indx = allModels.indexOf(singleRes.model);
if (indx >= 0) {
allModels.splice(indx, 1);
}
});
// Hide nodes from all other models
while (allModels.length) {
allModels.pop().visibilityManager.setAllVisibility(false);
}
this.viewer.clearSelection();
For the select, you need to pass corresponding model and dbIds to the viewer.impl.selector.setSelection([dbIds], model); and call setSelection for each set, such as below. It cannot be archived at once.
var selSet = [
{
selection: [1234, 5621],
model: model1
},
{
selection: [12, 758],
model: model2
},
];
selSet.forEach(funciton(sel) {
viewer.impl.selector.setSelection(sel.selection, sel.model);
});

GORM criteria group by in subquery

Let's assume the following Grails domains:
Owner {
String name
static hasMany [cars: Cars]
}
Car {
Date inspectionDate
}
I want to be able to search for Owners through Criteria, with the following rule: Most recent Car by inspectionDate in Owner's cars list being lower than *given date*.
As an example, I want to apply the following code to a select query in GORM:
queryResultsList = allOwnersList.filter { owner ->
owner.cars.min{ car -> car.inspectionDate }.inspectionDate < myDate
}
I need to achieve it using Criteria because I am already filtering Owners on other fields.
The whole given code is used as an example, some parts of the original code has been ommited, and source code is not about cars and owners.
As on first thought I assumed I needed a subquery in SQL to retrieve my data as I expected, I tried the following:
Owner.createCriteria().list {
// [...] some filters on other fields
cars {
lt('inspectionDate', params.inspectionDate)
'in'('inspectionDate', new grails.gorm.DetachedCriteria(Owner).list {
projections {
cars {
min('inspectionDate')
}
}
})
}
}
I also tried to add groupProperty in different places in the projection, ending with MissingPropertyException.
I am using Grails 2.2.4
After a few days testing solutions, I've come to the following one:
Owner.createCriteria().list {
// [...] some filters on other fields
cars {
lt('inspectionDate', params.inspectionDate)
'in'('inspectionDate', Owner.createCriteria().list {
projections {
cars {
min('inspectionDate')
}
groupProperty 'id'
}
// this allows to only get first property, which is inspectionDate
}.collect { it[0] })
}
}
However, I still feel like doing things wrong. In particular, the collect part looks like a code smell.
I am posting this because it does what I need, but any help with finding a good solution will be appreciated.

How to access the elements in the set returned by an Alloy function?

I have an Alloy function in my model like:
fun whichFieldIs[p:Program, fId:FieldId, c:Class] : Field{
{f:Field | f in c.*(extend.(p.classDeclarations)).fields && f.id = fId}
}
This function is working in my model and can return a set of elements such as:
{Field$0, Field$1}
although the function return is not "set Field". I already saw this through the Alloy evaluator tool (available in alloy4.2.jar). What i am trying to do is getting the first element of this set in another predicate, for instance:
pred expVarTypeIsOfA[p:Program, exprName:FieldId, mClass:Class, a:ClassId]{
let field = whichFieldIs[p, exprName, mClass],
fieldType = field[0].type
{
...
}
}
Even when i change the return of the function to "set Field", the error "This expression failed to be typechecked" appears. I only want to get the first element of a set returned by a function, any help?
Does the order really matter in that case? If so, you should take a look at this: seq
In the following example, for each person p, "p.books" is a sequence
of Book:
sig Book { }
sig Person {
books: seq Book
}
...So if s is a sequence of Book, then the first element is s[0]...
seq is now a reserved word, but is nothing more than a relation Int -> Elem.
If it does not matter, you could use an adequate quantifier, e.g.:
pred expVarTypeIsOfA[p:Program, exprName:FieldId, mClass:Class, a:ClassId]{
some field: whichFieldIs[p, exprName, mClass] | {
field.type ...
}
}