Finding an element in a list SML - iteration

I am trying to learn beginner functions in ML. I am trying to write a function where you have an item and a list and you iterate through the list to see if there is a match. But right now I have no idea how to iterate through a list.
A function like
fun foundList(L, []) = false
| foundList(L, x::xs) if L = xs then true;
I set the first line to false, because if the list is [ ] then it is empty and the statement is false?

You don't iterate, you recurse.
A value is in the list if it's the first element or if it's in the tail of the list:
fun foundList (_, []) = false
| foundList (L, x::xs) = L = x orelse foundList (L, xs)

Related

find string within list of strings

validValueType.ValueTypeGroup
["\"is_enabled\": false", "\"value\":\"OUT\""]
failedRecord.record
{"email":"test#gmail.com","source":"web","value":"OUT","reate_date":"2022-09-29T03:42:09.976-05:00","is_undeliverable":false}
fun publishAlert(failedRecord: Record<String>) {
if (validValueType.ValueTypeGroup.contains(failedRecord.record)) {
// do stuff
} else {
// no match do other stuff
}
}
In the list above there are two strings I want to check for when this function receives a record.
The failedRecord.record string does contain what I want "value":"OUT" and it's also within the list above. So why is contains not working here? it keeps bouncing out to the else statement.
You can use any() in the list and pass a predicate:
{x -> searchString.contains(x)}
The searchString.contains() will search x as a substring inside searchString for each x representing the elements in the list
var list = listOf("\"is_enabled\": false", "\"value\":\"OUT\"")
println(list)
println(list::class.qualifiedName)
println() // empty newline
var searchString = "{\"email\":\"test#gmail.com\",\"source\":\"web\",\"value\":\"OUT\",\"create_date\":\"2022-09-29T03:42:09.976-05:00\",\"is_undeliverable\":false}";
println(searchString)
println(searchString::class.qualifiedName)
println() // empty newline
println(list.any{x -> searchString.contains(x)});
Output
["is_enabled": false, "value":"OUT"]
java.util.Arrays.ArrayList
{"email":"test#gmail.com","source":"web","value":"OUT","create_date":"2022-09-29T03:42:09.976-05:00","is_undeliverable":false}
kotlin.String
true

Getting a set of a list from a list of strings

I have this dataframe:
df = pd.DataFrame({"c1":["[\"text\",\"text2\"]","[\"bla\",\"bla\",\"bla\"]"]})
and I'm removind [] and "" :
df["c2"] = df["c1"].apply(lambda x:re.sub('[\["\]]', "", x))
then I want to add df['c2'] to a list:
list = df['c2'].to_list()
Then I get this: ['text,text2', 'bla,bla,bla']
So far so good. But then I want a list with only unique values, what I could to using set(list).
The proble is that Instead of ['text,text2', 'bla,bla,bla'] I needed to get ['text','text2', 'bla','bla','bla'] so when I apply `set(list) I would get what I am expecting:
['text','text2','bla']
First, don't use list as a variable. Second, once you get ['text,text2',...] you can use str.split. So your set would be
{y for x in df['c2'].str.split(',') for y in x}
Output:
{'bla', 'text', 'text2'}
Note: You can use regex directly to extract all patterns between the \":
set(df['c1'].str.extractall('\"([^"]+)\"')[0])
Try this:
new = []
for l in list:
new.extend(l.split(',') )
new = list(set(new))
which results in new to be
['text2', 'text', 'bla']

Sum up the elements of a record

type MyType = {a:int; b:int};;
let test = [{a=5;b=10}; {a=10;b=100}; {a=200; b=500}; {a=100; b=2}];;
I would like to create a function which will sum up a and b in such a way it will display
Sum of a : 315
Sum of b : 612
I think I have to use a recursive function. Here is my attempt :
let my_func record =
let adds = [] in
let rec add_func = match adds with
| [] -> record.a::adds; record.b::adds
| _ -> s + add_func(e::r)
add_func test;;
This function doesn't seem to work at all. Is there a right way to do such function?
OK, you know exactly what you're doing, you just need one suggestion I think.
Assume your function is named sumab and returns a pair of ints. Then the key expression in your recursive function will be something like this:
| { a = ha; b = hb; } :: t ->
let (a, b) = sumab t in (ha + a, hb + b)

Best way to get next element of a List in Elm

I am currently trying to figure out the best way of traversing a List.
What do I mean by traversing?
Example:
I have a List of Users:
userList : List User
userList =
[user, user, user, user]
and I have a currentUser, which must be a user out of the userList
So what I want to achieve is:
I want to have something like List.getNext which takes the userList and the current user and returns the next user in the list, relative to the currentUser
Here is my implementation. I think it is very complicated - so has anyone an idea how to do this in a better style?
traverseList : List a -> a -> Maybe (Maybe a)
traverseList list currentElement =
let
indexList =
List.indexedMap
(\index element ->
if element == currentElement then
index
else
-1
)
list
currentAsIndex =
let
mayBeIndex =
List.maximum indexList
in
case mayBeIndex of
Just index ->
index
Nothing ->
0
getWanted =
List.map
(\( id, element ) ->
if id == (currentAsIndex + 1) then
Just element
else
Nothing
)
(List.indexedMap (,) list)
|> List.filter
(\element ->
element /= Nothing
)
|> List.head
in
getWanted
Explanation:
My approach is to get the list, make an index list of the given list (looks like this [-1, -1, -1, 3, -1, -1])
Then I get the maximum of this list - as this gives me the position of the current user in a List.indexedMap.
Then I iterate the original as an List.indexedMap and figure out the next one (in our case No. 4) and return that element. Else I return nothing.
Then I filter this List of Nothings and just one user and extract the user from the list by using List.head.
The Result is a Maybe (Maybe user)... that is not so nice... or?
Thanks for any ideas to do something like this in a better functional way.
I really try to get better in functional programming..
Here is a pretty naive, recursive solution:
getWanted : List a -> a -> Maybe a
getWanted list currentElement =
let findNextInList l = case l of
[] -> Nothing
x :: [] -> if x == currentElement
then List.head list
else Nothing
x :: y :: rest -> if x == currentElement
then Just y
else findNextInList (y :: rest)
in
findNextInList list
The idea here is look at the first two elements of the list, and if the first is the current element, take the second. If not, try again with the tail of the list.
Corner cases must be handled (you can write at least 4 unit tests for this function):
current element not found at all
current element is the last in the list
the list might be empty
Maybe there is a more elegant solution, but recursion is a pretty common technique in functional programming, so I wanted to share this approach.
If you are willing to import list-extra you can use
import List.Extra as LE exposing ((!!))
getNext : a -> List a -> Maybe a
getNext item list =
list
|> LE.elemIndex item
|> Maybe.map ((+) 1)
|> Maybe.andThen ((!!) list)
If the found element is the last in the list it returns Nothing
Another possible solution:
getNext : List a -> a -> Maybe a
getNext list element =
list
|> List.drop 1
|> zip list
|> List.filter (\(current, next) -> current == element)
|> List.map (\(current, next) -> next)
|> List.head
zip : List a -> List b -> List (a,b)
zip = List.map2 (,)
We drop the first element of the list and then zip it with the original list to get a list of tuples. Each element of the tuple contains the current element and the next element. We then filter that list, take the 'next' element from the tuple and take the head of the final list.
I like farmio's library-based approach best, and is what I'll be using in future.
That said, for being new to elm and not looking over its libraries, my erstwhile approach has been as in the following: i.e. index the list, find index if any of searched-for item, get item at next index if any.
nextUser : List User -> User -> Maybe User
nextUser lst usr =
let
numberedUsers =
List.map2 (\idx u -> ( idx, u )) (List.range 1 (List.length lst)) lst
usrIdx =
List.filter (\( idx, u ) -> u.name == usr.name) numberedUsers
|> List.head
|> Maybe.map Tuple.first
|> Maybe.withDefault -1
in
List.filter (\( idx, u ) -> idx == usrIdx + 1) numberedUsers
|> List.head
|> Maybe.map Tuple.second
Another way with as much fiddlery would be to use folds.
Maybe instead of List just use Array, which provides index-based access.
There are some good answers to the concrete question. I'd like to suggest an alternative model, since this approach has some downsides:
You rely on checking equality of users. This limits the user type, e.g., there can't be functions in there.
Looking up a user in a list that doesn't guarantee that all users are different is fragile: If the list happens to end up as [user1, user2, user1, user3], the rotation will never reach user3.
Nothing guarantees that the current user is in the list, hence the Maybe in the return type of the other answers which is hard to deal with correctly.
Thus, let's assume your current model is
type alias Model =
{ currentUser : User
, users : [User]
}
and what you're asking for is
rotateUser : Model -> Model
rotateUser model =
let
nextUser = Maybe.withDefault
model.currentUser
(getNext model.users model.currentUser)
in
{ model | currentUser = nextUser }
getNext : List a -> a -> Maybe a
...
How about instead of implementing getNext, we change the model to make life easier:
type alias Model =
{ currentUser : User
, otherUsers : [User]
}
rotateUser : Model -> Model
rotateUser model =
case model.otherUsers of
[] -> model
(nextUser::rest) ->
{ model
| currentUser = nextUser
, otherUsers = rest ++ [model.currentUser]
}

More FP-correct way to create an update sql query

I am working on access a database using F# and my initial attempt at creating a function to create the update query is flawed.
let BuildUserUpdateQuery (oldUser:UserType) (newUser:UserType) =
let buf = new System.Text.StringBuilder("UPDATE users SET ");
if (oldUser.FirstName.Equals(newUser.FirstName) = false) then buf.Append("SET first_name='").Append(newUser.FirstName).Append("'" ) |> ignore
if (oldUser.LastName.Equals(newUser.LastName) = false) then buf.Append("SET last_name='").Append(newUser.LastName).Append("'" ) |> ignore
if (oldUser.UserName.Equals(newUser.UserName) = false) then buf.Append("SET username='").Append(newUser.UserName).Append("'" ) |> ignore
buf.Append(" WHERE id=").Append(newUser.Id).ToString()
This doesn't properly put a , between any update parts after the first, for example:
UPDATE users SET first_name='Firstname', last_name='lastname' WHERE id=...
I could put in a mutable variable to keep track when the first part of the set clause is appended, but that seems wrong.
I could just create an list of tuples, where each tuple is oldtext, newtext, columnname, so that I could then loop through the list and build up the query, but it seems that I should be passing in a StringBuilder to a recursive function, returning back a boolean which is then passed as a parameter to the recursive function.
Does this seem to be the best approach, or is there a better one?
UPDATE:
Here is what I am using as my current solution, as I wanted to make it more generalized, so I just need to write an abstract class for my entities to derive from and they can use the same function. I chose to split up how I do the function so I can pass in how to create the SET part of the update so I can test with different ideas.
let BuildUserUpdateQuery3 (oldUser:UserType) (newUser:UserType) =
let properties = List.zip3 oldUser.ToSqlValuesList newUser.ToSqlValuesList oldUser.ToSqlColumnList
let init = false, new StringBuilder()
let anyChange, (formatted:StringBuilder) =
properties |> Seq.fold (fun (anyChange, sb) (oldVal, newVal, name) ->
match(oldVal=newVal) with
| true -> anyChange, sb
| _ ->
match(anyChange) with
| true -> true, sb.AppendFormat(",{0} = '{1}'", name, newVal)
| _ -> true, sb.AppendFormat("{0} = '{1}'", name, newVal)
) init
formatted.ToString()
let BuildUserUpdateQuery (oldUser:UserType) (newUser:UserType) (updatequery:UserType->UserType->String) =
let buf = StringBuilder("UPDATE users SET ");
buf.AppendFormat(" {0} WHERE id={1}", (updatequery oldUser newUser), newUser.Id)
let UpdateUser conn (oldUser:UserType) (newUser:UserType) =
let query = BuildUserUpdateQuery oldUser newUser BuildUserUpdateQuery3
execNonQuery conn (query.ToString())
Is this the tuple solution you had in mind?
let BuildUserUpdateQuery (oldUser:UserType) (newUser:UserType) =
let buf = StringBuilder("UPDATE users set ")
let properties =
[(oldUser.FirstName, newUser.FirstName, "first_name")
(oldUser.LastName, newUser.LastName, "last_name")
(oldUser.UserName, newUser.UserName, "username")]
|> Seq.map (fun (oldV, newV, field) ->
if oldV <> newV
then sprintf "%s='%s'" field newV
else null)
|> Seq.filter (fun p -> p <> null)
|> Seq.toArray
if properties.Length = 0
then None
else
bprintf buf "%s" (String.Join(", ", properties))
bprintf buf " where id=%d" newUser.Id
Some <| buf.ToString()
I don't see how the recursive solution could be simpler than this...
BTW I would strongly advise to use proper SQL parameters instead of just concatenating the values, you might become vulnerable to injection attacks...
Just for completeness, here is a version that does the same thing directly using the fold function. This can be done quite elegantly, because methods of StringBuilder return the StringBuilder (which allows you to chain them in C#). This can be also used nicely for folding.
Let's assume that we have the list of tuples from the solution by Mauricio:
let properties =
[ (oldUser.FirstName, newUser.FirstName, "first_name")
(oldUser.LastName, newUser.LastName, "last_name")
(oldUser.UserName, newUser.UserName, "username") ]
Now you can write the following code (it also returns a flag whether anything has changed):
let init = false, new StringBuilder()
let anyChange, formatted =
properties |> Seq.fold (fun (anyChange, sb) (oldVal, newVal, name) ->
if (oldVal = newVal) anyChange, sb
else true, sb.AppendFormat("{0} = '{1}'", name, newVal)) init
The state kept during folding has type bool * StringBuilder and we start with an initial value containing empty string builder and false. In each step, we either return the original state (if value is the same as previous) or a new state containing true and a new version of the StringBuilder returned by AppendFormat.
Using recursion explicitly would also work, but when you can use some built-in F# function, it is usually easier to use this approach. If you needed to process nested entities of each entity, you could use the Seq.collect function together with recursion to get a list of properties that you need to process using fold. Pseudo-code might look like this:
let rec processEntities list names =
// Pair matching entity with the name from the list of names
List.zip list names
|> List.collect (fun (entity, name) ->
// Current element containing old value, new value and property name
let current = (entity.OldValue, entity.NewValue, name)
// Recursively proces nested entitites
let nested = processEntities entity.Nested
current::nested)
This can be more elegantly written using sequence expressions:
let rec processEntities list =
seq { for entity, name in List.zip list names do
yield (entity.OldValue, entity.NewValue, name)
yield! processEntities entity.Nested }
Then you could simply call processEntities which returns a flat list of entities and process the entities using fold as in the first case.
I like both Mauricio's and Tomas's solutions, but perhaps this is more like what you originally envisioned?
let sqlFormat (value:'a) = //'
match box value with
| :? int | :? float -> value.ToString()
| _ -> sprintf "'%A'" value // this should actually use database specific escaping logic to make it safe
let appendToQuery getProp (sqlName:string) (oldEntity,newEntity,statements) =
let newStatements =
if (getProp oldEntity <> getProp newEntity) then (sprintf "%s=%s" sqlName (sqlFormat (getProp newEntity)))::statements
else statements
(oldEntity, newEntity, newStatements)
let createUserUpdate (oldUser:UserType) newUser =
let (_,_,statements) =
(oldUser,newUser,[])
|> appendToQuery (fun u -> u.FirstName) "first_name"
|> appendToQuery (fun u -> u.LastName) "last_name"
|> appendToQuery (fun u -> u.UserName) "username"
// ...
let statementArr = statements |> List.toArray
if (statementArr.Length > 0) then
let joinedStatements = System.String.Join(", ", statementArr)
Some(sprintf "UPDATE users SET %s WHERE ID=%i" joinedStatements newUser.ID)
else
None
If you have lots of properties to check, this may be a bit more concise. One benefit to this approach is that it works even if you're checking properties of multiple types, whereas the other approaches require all properties to have the same type (since they're stored in a list).