Sorting List of Objects Containing Maybe Time.Posix - elm

I have a List of custom types that I would like to sort on one attribute which is of type Maybe Time.Posix. In reading the docs I've come to the conclusion I should use List.sortWith, as neither Maybe nor Time.Posix values are comparable. I've written a set of functions to prepare the values so they are comparable. The challenge that I'm facing, though, is pulling the values from the types in the list.
Here are the functions:
maybeCompare : (a -> a -> Order) -> Maybe a -> Maybe a -> Order
maybeCompare f a b =
case a of
Just some_a ->
case b of
Just some_b ->
f some_a some_b
Nothing ->
GT
Nothing ->
LT
posixCompare : Time.Posix -> Time.Posix -> Order
posixCompare a b = compare (posixToMillis(a)) (posixToMillis(b))
posMay = maybeCompare (posixCompare)
Which I combine and use like this:
List.sortWith (posMay .time) objList
On data that looks like this:
obj1 = {id=1,time= Just time1}
obj2 = {id=2,time= Just time2}
obj3 = {id=3,time= Just time3}
obj4 = {id=4,time= Just time4}
obj5 = {id=5,time= Nothing}
objList = obj1 :: obj2 :: obj3 :: obj4 :: obj5 :: []
Now, this approach works for a list like this List (Maybe Time.Posix). By which I mean I get the output I expect, the list is sorted on the Posix time with the Nothing values in the desired location.
However, for a List of types where Maybe Time.Posix is one of the values I get this error (one of many, but I think this is the source):
List.sortWith (posMay .time) objList
^^^^^
This .time field access function has type:
{ b | time : a } -> a
But `posMay` needs the 1st argument to be:
Maybe Time.Posix
Is there are way to make the types of my functions align to sort this kind of data? Or, should I rethink my approach?

I've created a working example at https://ellie-app.com/8dp2qD6fDzBa1
Your posMay function is of the type Maybe a -> Maybe a -> Order, so it's not expecting .time, which is a function of type {id:Int,time:Maybe Posix} -> Maybe Posix.
Instead, you can create a different function which shims between posMay and List.sortWith, which would look like this: List.sortWith (\a b -> posMay a.time b.time)
If you want to be able to pass a getter function to posMay, you could rewrite it to accept that function:
posMay getter a b =
maybeCompare posixCompare (getter a) (getter b)
Then, you would use it like this: List.sortWith (posMay .time) (or List.sortWith (posMay identity) for a List (Maybe Posix). A version that works like this is at https://ellie-app.com/8dp7gm3qthka1

Related

How to print lists in a scific order in kotlin?

im working on a project and i have a list in kotlin like:
val list = listOf("banana", "1","apple","3","banana","2")
and i want to print it like
Output:
banana = 1
banana = 2
apple = 3
so like every work with the number should be like one val, and i need to print in scific order (the order is toooo random for any sort command), so im panning on just coppying the whole xinhua dictionary here (since all chinese words have a scific unicode), and make the code it replace like:
val list = listOf("banana丨", "1","apple丩","3","banana丨","2")
but how to print them in the order?
ps. even me as a chinese dont know most of the words in xinhua dictionary lol so there is more then enofe
Assuming that you have the following input list, as shown in your question, where the order of occurrence is always one word followed by the scific order:
val list = listOf("banana", "1","apple","3","banana","2")
You could do the following:
1. Create a data class that defines one entry in your raw input list
data class WordEntry(val word: String, val order: Int)
2. Map over your raw input list by using the windowed and map methods
val dictionary = list.windowed(2, 2).map { WordEntry(it.first(), it.last().toInt()) }
Here, the windowed(2, 2) method creates a window of size 2 and step 2, meaning that we iterate over the raw input list and always work with two entries at every second step. Assuming that the order in the raw input list is always the word followed by the scific order, this should work. Otherwise, this would not work, so the order is very important here!
3. Sort the transformed dictionary by the order property
val sortedDictionary = dictionary.sortedBy { it.order }
Edit: You can also sort by any other property. Just pass another property to the lambda expression of sortedBy (e.g. sortedBy { it.word } if you want to sort it by the word property)
4. Finally, you can print out your sorted dictionary
val outputStr = sortedDictionary.joinToString("\n") { "${it.word} = ${it.order}" }
print(outputStr)
Output:
banana = 1
banana = 2
apple = 3

how to add class to all columns of a table in elm script

The elm scripts gets a csv string csv from backend code. I want to create a table and the class name of each cell should be the column name.
The rows of the table can be obtained from rows = String.split "\n" csv. If I don't need to define classes, a table can be created by applying doubly nested List.map on rows. But in order to give class to each cell, I need to save the first element of row which contains the column (or class) names and pair it with each of the remaining rows. Since there is no for loop in elm, my strategy is to define a recursive function to create all tr elements and concatenate the tr elements using List.map. The input of this function is the first row called names and another row called cols. They are both List String. Each time it runs, it creates a td using the heads (from List.head) of the two Lists and then pass the remaining elements of the two lists (from List.drop) for recursion. But I couldn't concatenate the two returned components.
createRow names cols =
case cols of
[] -> text ""
_ ->
let
c0 = (List.head cols)
c = (List.drop 1 cols)
n0 = (List.head names)
n = (List.drop 1 names)
in
td [] [text <| toString <| c0]
, createRow n c
What should I put in the in block? Or is there a better way to achieve my purpose?
First I would suggest you don't parse the CSV manually. There are multiple CSV parsers available on the package manager so we can instead focus on what to do with the values.
https://package.elm-lang.org/packages/periodic/elm-csv/latest/Csv
is an option, but they all give you a Csv type after parsing, which looks like this:
type alias Csv =
{ headers : List String
, records : List (List String)
}
As you have touched on, there are as many headers as there are values in each row (otherwise the CSV wouldn't be valid). Dropping/casing through recursion can get the job done, but we can be more declarative by relying on list operations to do the bulk of the work:
classTable : Csv -> Html msg
classTable csv =
table []
(csv.records |> List.map (tableRow csv.headers))
tableRow : List String -> List String -> Html msg
tableRow headers values =
let
insertNextCellInRow ( header, value ) row =
td [ class header ] [ text value ] :: row
in
tr []
(List.map2 Tuple.pair headers values
|> List.foldr insertNextCellInRow []
)
Note: List.foldr is recursive and serves the purpose of a loop in an imperative language ("for each item in this list, apply this function and collect the results in this other list"). It is however agnostic of the types of values passed to it and allows us to focus on transforming the values.
In a recursive function, you need to pass the (partially-computed) result along, so that you can modify it in each call, and return it once you've finished recursing. So that might look something like this:
createRow : List String -> List String -> List (Html msg) -> List (Html msg)
createRow names cols cells =
case cols of
[] -> cells
col :: remainingCols ->
let
name =
names
|> List.head
|> Maybe.withDefault ""
remainingNames = List.drop 1 names
cell =
td [class name] [text col]
in
createRow remainingNames remaningCols (cells ++ [ cell ])
A couple of other notable changes here:
I've used pattern matching to extract the head of the cols list from the rest (also called the tail)
List.head returns a Maybe a (in this case, a is String), so I've added a call to Maybe.withDefault
Since cols is a List String, you don't need to call toString and can pass it directly to text
When you call this function for the first time, you'll pass in [] for the cells argument

RxJava2 how to join 2 Single List into one list

I have 2 different data sources that I want to combine.
val source1: Single<List<Type1>> = Single.fromCallable({
api.getSource1()
})!!
val source2: Single<List<Type2>> = Single.fromCallable({
api.getSource2()
})!!
//PS.
class Type0()
class Type1 : Type0()
class Type2 : Type0()
I want to join 2 sources and get
Single<List<Type0>>
so I could do further processing of the data, I think I should use .zip method, but I am not sure how to do it correctly.
The zipWith operator works well here, as it lets you provide a BiFunction that describes how to combine the two Single instances (in this case, we just concatenate them with the plus operator):
val zipped: Single<List<Type0>> = source1.zipWith(source2, BiFunction { l1, l2 -> l1 + l2 })

Need a TRUE and FALSE column in Spark-SQL

I'm trying to write a multi-value filter for a Spark SQL DataFrame.
I have:
val df: DataFrame // my data
val field: String // The field of interest
val values: Array[Any] // The allowed possible values
and I'm trying to come up with the filter specification.
At the moment, I have:
val filter = values.map(value => df(field) === value)).reduce(_ || _)
But this isn't robust in the case where I get passed an empty list of values. To cover that case, I would like:
val filter = values.map(value => df(field) === value)).fold(falseColumn)(_ || _)
but I don't know how to specify falseColumn.
Anyone know how to do so?
And is there a better way of writing this filter? (If so, I still need the answer for how to get a falseColumn - I need a trueColumn for a separate piece).
A column that is always true:
val trueColumn = lit(true)
A column that is always false:
val falseColumn = lit(false)
Using lit(...) means these will always be valid columns, regardless of what columns the DataFrame contains.

Erlang's term_to_binary in Haskell?

Is there a no-fuss serialization method for Haskell, similar to Erlang's term_to_binary/binary_to_term calls? Data.Binary seems unnecessarily complicated and raw. See this example where you are basically manually encoding terms to integers.
Use Data.Binary, and one of the deriving scripts that come with the package.
It's very simple to derive Binary instances, via the 'derive' or 'deriveM' functions provided in the tools set of Data.Binay.
derive :: (Data a) => a -> String
For any 'a' in Data, it derives a Binary instance for you as a String. There's a putStr version too, deriveM.
Example:
*Main> deriveM (undefined :: Drinks)
instance Binary Main.Drinks where
put (Beer a) = putWord8 0 >> put a
put Coffee = putWord8 1
put Tea = putWord8 2
put EnergyDrink = putWord8 3
put Water = putWord8 4
put Wine = putWord8 5
put Whisky = putWord8 6
get = do
tag_ <- getWord8
case tag_ of
0 -> get >>= \a -> return (Beer a)
1 -> return Coffee
2 -> return Tea
3 -> return EnergyDrink
4 -> return Water
5 -> return Wine
6 -> return Whisky
_ -> fail "no parse"
The example you cite is an example of what the machine generated output looks like -- yes, it is all bits at the lowest level! Don't write it by hand though -- use a tool to derive it for you via reflection.