How can we sort dictionaries in an array by a specific key in pharo? - smalltalk

I have an array containing several dictionaries. How can I sorted them using a key that each dictionary have like age?
an Array((a Dictionary('age'->'20' 'ID'->1254))(a Dictionary('age'->'35' 'ID'->1350))(a Dictionary('age'->'42' 'ID'->1425)))

You can sort by providing a comparator block; the block takes two arguments (two elements from the array) and is expected to return boolean.
data := {
{ 'age' -> '20'. 'ID' -> 1254 } asDictionary.
{ 'age' -> '35'. 'ID' -> 1350 } asDictionary.
{ 'age' -> '42'. 'ID' -> 1425 } asDictionary
}.
sorted := data sorted: [ :a :b | (a at: 'age') > (b at: 'age') ].
sorted: will return a sorted collection without changing the receiver
sort: will perform the sorting in-place and return itself
You can also use asSortedCollection: which will create a new collection that always upholds the sorting invariant.
sc := data asSortedCollection: [ :a :b | (a at: 'age') > (b at: 'age') ].
"automatically inserted between age 42 and 35"
sc add: {'age' -> '39'. 'ID' -> 1500} asDictionary.
sc "a SortedCollection(a Dictionary('ID'->1425 'age'->'42' ) a Dictionary('ID'->1500 'age'->'39' ) a Dictionary('ID'->1350 'age'->'35' ) a Dictionary('ID'->1254 'age'->'20' ))"

Related

Converting a Spark Dataframe to a Scala Map collection list

I'm trying to transform a Spark dataframe into a Scalar map and additionally a list of values.
It is best illustrated as follows:
val df = sqlContext.read.json("examples/src/main/resources/people.json")
df.show()
+----+-------+
| age| name|
+----+-------+
|null|Michael|
| 30| Andy|
| 19| Justin|
| 21|Michael|
+----+-------+
To a Scala collection (Map of Maps(List(values))) represented like this:
Map(
(0 -> List(Map("age" -> null, "name" -> "Michael"), Map("age" -> 21, "name" -> "Michael"))),
(1 -> Map("age" -> 30, "name" -> "Andy")),
(2 -> Map("age" -> 19, "name" -> "Justin"))
)
As I don't know much about Scala, I wonder if this method is possible. It doesn't matter if it's not necessarily a List.
The data structure you want is actually useless. Let me explain what I mean by asking 2 questions:
What is the purpose of the integers of the outside map? are those indices? What is the logic of those indices? If those are indices, why not just use Array?
Why to use Map[String, Any] and do unsafe element accessing, while you can model into case classes?
So I think the best thing you can do would be this:
case class Person(name: String, age: Option[Int])
val persons = df.as[Person].collect
val personsByName: Map[String, Array[Person]] = persons.groupBy(_.name)
Result would be:
Map(
Michael -> Array(Person(Michael, None), Person(Michael, Some(21)),
Andy -> Array(Person(Andy, Some(30))),
Justin -> Array(Person(Justin, Some(19)))
)
But still, if you insist on the data structure, this is the code you need to use:
val result: Map[Int, List[Map[String, Any]]] =
persons.groupBy(_.name) // grouping persons by name
.zipWithIndex // coupling index with values of array
.map {
case ((name, persons), index) =>
// put index as key, map each person to the desired map
index -> persons.map(p => Map("age" -> p.age, "name" -> p.name)).toList
}

Find item in list and add to other list

I have a model containing a list of items that are rendered in a select as options.
The user can select an item, enter a number and click add to add the selected item and a "quantity" to a list.
My model looks like this:
type alias Drink =
{ id: String
, name: String
}
type alias Item =
{ id: String
, quantity: Int
}
type alias Model =
{ drinks: List Drink
, selected: List Item
, inputDrink: String
, inputQuantity: Int
}
I then want to render the selected list in a table. My main struggle right now is figuring out how I map over the array of selected items, based on the id of the current item find the name of the Drink to render in the table.
I've made this itemRow view:
itemRow : (Item, Drink) -> Html Msg
itemRow tuple =
-- This bit not updated to work with a Tuple yet.
tr [ id item.id ]
[ td []
[ button [] [ text "x" ]
]
, td [] [ text drink.name ]
, td []
[ input [ type_ "number", value (String.fromInt item.quantity) ] []
]
]
So what I'd like is to do something like:
model.selected
|> List.map (\selected -> (selected, List.Extra.find (\drink -> drink.id == selected.id)) )
|> List.map itemRow
But to do this I need to get rid of the Maybe I get from List.Extra.find and I don't know how… 😅
Any other tips or tricks on how I might better solve this by modelling the data differently very welcome. New to Elm :)
Here's how you remove the Nothings. Although you know that the find must always succeed, Elm requires you to handle the case where it does not. Here I just ignore those cases.
model.selected
|> List.filterMap (\selected ->
case List.Extra.find (\drink -> drink.id == selected.id) of
Just x -> Just (selected, x)
Nothing -> Nothing
)
|> List.map itemRow

SQL query with json column

I have a table individual customer with a column employmentDetails as json field.
The task is to get a customer that has empty locality field
[{
"employmentStatus": "E",
"communicationInfo": {
"addresses": [
{
"id": "1",
"houseName": "1",
"locality": null
}
]
}}]
I tried several variants with casting etc with no success, please help me to understand how to query from json object fields. My attempts below that should return some values but returned nothing.
SELECT * FROM crm."IndividualCustomer" AS ic
WHERE (ic."employmentDetails" -> 'employmentStatus')::text = 'E';
SELECT * FROM crm."IndividualCustomer" AS ic
WHERE ic."employmentDetails" -> 'communicationInfo' -> 'adresses[0]' ->> 'locality' = null;
SELECT * FROM crm."IndividualCustomer" AS ic
WHERE ic."employmentDetails" -> 'communicationInfo' -> 'adresses' ->> 'locality' = null;
The task is to get a customer that has empty locality field
You can use a JSON path expression:
SELECT *
FROM crm."IndividualCustomer" AS ic
WHERE ic."employmentDetails" ## '$[*].communicationInfo.addresses[*].locality == null'
This requires Postgres 12 or later. If you have an older version, you can use the contains operator #>:
WHERE ic."employmentDetails" #> '[{"communicationInfo": {"addresses": [{"locality": null}]}}]'
This assumes that "employmentDetails" is defined as jsonb (which it should be). If it's not, you need to cast it: "employmentDetails"::jsonb.
The condition: (ic."employmentDetails" -> 'employmentStatus')::text = 'E' doesn't work, because -> returns a jsonb (or json) value which can't really be cast to a proper text value (the double quotes are kept if you do so).
You need to use the ->> operator which returns a text value directly: (ic."employmentDetails" ->> 'employmentStatus') = 'E'
However, the top level object is an array, so you would need to pick the e.g. the first array element:
(ic."employmentDetails" -> 0 ->> 'employmentStatus') = 'E'
Note the -> to return the first array element as a proper jsonb value, then the use of ->> on that value.
This can also be done using a JSON path expression to search through all array elements:
WHERE ic."employmentDetails" ## '$[*].employmentStatus == "E"'`

Using RxJava to generate a map where keys are values of a Kotlin enum and map's values come from another RxJava stream

Introduction
Let's say I have a Kotlin enum class:
enum class Type {
ONE,
TWO,
THREE,
FOUR
}
and a following data class:
data class Item(
val name: String,
val type: Type
)
Then I have a Single that emits a list of Items – can by anything but for example purposes, let's say it looks like that:
val itemsSingle = Single.just(listOf(
Item("A", Type.ONE),
Item("B", Type.ONE),
Item("C", Type.TWO),
Item("D", Type.THREE),
))
Problem
What I'd like to achieve is to have an RxJava stream that will output a map where keys come from Type and values are lists of Items matching a given Type value (where an undetermined, unsorted list of Items is provided by some other Single stream). The signature would be:
Single<Map<Type, List<Item>> // or Observable<Map<Type, List<Item>>
One additional requirement is that the map's keys should always exhaust all values from Type enum even if the itemsSingle stream contains no items for some Type values (or no items at all). So, for the provided itemsSingle example stream the returned map should look like this:
{
ONE: [ Item(name: "A", type: ONE), Item(name: "B", type: ONE) ],
TWO: [ Item(name: "C", type: TWO) ],
THREE: [ Item(name: "D", type: THREE) ],
FOUR: []
}
Attempt
With all the above, I've kinda achieved the desired result with following steps:
To satisfy the requirement of exhausting all Type enum values I first create a map that has an empty list for all possible Type values:
val typeValuesMap = Type.values().associate { it to emptyList<Item>() }
val typeValuesMapSingle = Single.just(typeValuesMap)
// result: {ONE=[], TWO=[], THREE=[], FOUR=[]}
I can get a map that contains items from itemsSingle grouped under respective Type value keys:
val groupedItemsMapSingle = itemsSingle.flattenAsObservable { it }
.groupBy { it.type }
.flatMapSingle { it.toList() }
.toMap { list -> list[0].type } // the list is guaranteed to have at least one item
// result: {ONE=[Item(name=A, type=ONE), Item(name=B, type=ONE)], THREE=[Item(name=D, type=THREE)], TWO=[Item(name=C, type=TWO)]}
finally I can combine both lists using the combineLatest operator and overwriting initial empty list of items for a given Type value if itemsSingle contained any Items for this Type value:
Observable.combineLatest(
typeValuesMapSingle.flattenAsObservable { it.entries },
groupedItemsMapSingle.flattenAsObservable { it.entries }
) { a, b -> listOf(a, b) }
.defaultIfEmpty(typeValuesMap.entries.toList()) // in case itemsSingle is empty
.flatMapIterable { it }
.collect({mutableMapOf<Type, List<Item>>()}, { a, b -> a[b.key] = b.value})
// result: {FOUR=[], ONE=[Item(name=A, type=ONE), Item(name=B, type=ONE)], THREE=[Item(name=D, type=THREE)], TWO=[Item(name=C, type=TWO)]}
Summary
As you can see, it's quite a lot of code for a seemingly simple operation. So my question is – is there a simpler way to achieve the result I'm after?
Just merge a map of empty lists with a map of filled lists
val result = itemsSingle.map { items->
Type.values().associateWith { listOf<Item>() } + items.groupBy { it.type }
}

How do we iterate and select an element of a set in pharo?

My collection is a Set that contains a number of dictionaries. How can iterate over each dictionary in the Set to select a specific key.
a Set(a Dictionary('age'->'25' 'code'->2512) a Dictionary('age'->'40' 'code'->'1243') a Dictionary('age'->'35' 'code'->'7854'))
set := {
{ 'age'->'25'. 'code'->'2512' } asDictionary .
{ 'age'->'40'. 'code'->'1243' } asDictionary.
{ 'age'->'35'. 'code'->'7854' } asDictionary.
} asSet.
If you are interested in retrieving just a single item, then detect: is the way to go. It will return the first item matching the predicate (the block). Note that Set has no defined order, so if you have multiple items matching, it may return different ones at different time.
d := set detect: [ :each | (each at: 'code') = '1243' ].
d. "a Dictionary('age'->'40' 'code'->'1243' )"
If you want to retrieve multiple items that all match the predicate, then use select:
multi := set select: [ :each | (each at: 'age') asNumber >= 35 ].
multi. "a Set(a Dictionary('age'->'40' 'code'->'1243' ) a Dictionary('age'->'35' 'code'->'7854' ))"
Update from comment for commenting:
As Carlos already stated, collect: will do what you need. It applies the transformation block to every item in the collection and then returns a collection of results.
codes := set collect: [ :each | each at: 'code' ].
Works for any collection
#(2 3 4) collect: [ :each | each squared ] "#(4 9 16)"
For further I recommend going through the Collections chapter in Pharo By Example book https://ci.inria.fr/pharo-contribution/job/UpdatedPharoByExample/lastSuccessfulBuild/artifact/book-result/Collections/Collections.html
mySet do: [:each | each do: [ :i | i doStuff ]]
or use detect (I`m not sure if detect works like this, I never used it so far):
mySet do: [:i | i detect: [ :each| (each at: 'key') doStuff ]].
or use keysDo:
mySet do: [:each | each keysDo: [ :k | k doStuff ]]
Check out: http://pharo.gforge.inria.fr/PBE1/PBE1ch10.html