Elm chat bot idea

Hello there,

I have recently asked my question here, and based on the answers I got some results.
So far I have the basic structure of what I want. I would like to add some more functionalities there, but have some more questions:

  • Is it possible to retrieve the list items without considering case sensitivity?

  • Can i make the retrieved list item linked to another list item, and when clicked only the linked item appears?

  • Can I make all the list items linked to some headlines? for example in my list there is one list item “this is our first sentences.”, Now, I would like to link this list item to another list Item called “first sentences”. and when I search for the first list item, the second list item appears?

  • How to clear list items, when there is no text on the TextBox?

I am ready to explain anything if its not clear.
Thank You.

My Source Code:

module Main exposing (main)

import Browser
import Html exposing (Html, Attribute, div, input, text,p,h1,br,h2,button)
import Html.Attributes exposing (..)
import Html.Events exposing (onInput,onClick)

type alias Model =
    { sentences : List String
    , results : List String
    }
sentences : List String
sentences =
  [ "Hello there this is our first sentence."
  , "this is the second sentence."
  , "in our village its night"
  , "and this is the fourth one."
  , "this is the second sentence."
  , "there is no third number for the sentence."
  , "the box office results were crazy."
  , "a quick brown fox jumps over the lazy dog"
  , "Jumanji Jungalee"
  , "our privacy policy"
  ]

initialModel : Model
initialModel =
    { sentences = sentences 
    , results = []
    }


type Msg =
  WordEntered String


update : Msg -> Model -> Model
update msg model =
    case msg of
     WordEntered word ->
      ( { model
          | results =
            model.sentences
              |> List.filter (String.contains word)
         }
      )
    
rowItem: String -> Html Msg
rowItem id =
    div [style "background-color" "#AFEEEE"
        ,style "border" "1px solid white",style "border-radius" "10px"
        ,style "padding" "10px"
        ]
        [ text id ]

view : Model -> Html Msg
view model =
  let
    results =
      model.results
        |> List.map
          (\ result -> 
                div[] []
          )
  in
  div [ style "text-align" "center"]
    [ h2 [ style "background-color" "#8B0000", style "border" "2px solid",style "color" "white"][text "Welcome to our chatbot!"]
    , div[ style "border" "2px solid black" , style "border-radius" "10px", style "width" "50%"][
      input [ placeholder "word", onInput WordEntered, style "padding" "5px 5px"] []
    , button [style "background-color" "#8B0000", style "padding" "5px 5px", style "border-radius" "8px", style "color" "white"][text "Search"]
    , div[](List.map rowItem model.results)
    , br [] []
    ]]

main : Program () Model Msg
main =
    Browser.sandbox
        { init = initialModel
        , view = view
        , update = update
        }

Generally, yes to all four question. But I need to read your code a bit more to give more detailed answers.

Is it possible to retrieve the list items without considering case sensitivity?

The short answer is “use String.toLower”.

In your code this would mean the following changes:

initialModel : Model
initialModel =
    { sentences = 
        sentences 
          |> List.map String.toLower
    , results = []
    }

update : Msg -> Model -> Model
update msg model =
    case msg of
     WordEntered word ->
      ( { model
          | results =
            model.sentences
              |> List.filter (String.contains (word |> String.toLower) )
         }
      )
1 Like

Thank you for your reply.
that works perfect. I am waiting for the other parts as well. :blush:

I wasn’t actually planing on answering all question today (its quite late for me).

Maybe I can answer the second question, lets see…

Can i make the retrieved list item linked to another list item, and when clicked only the linked item appears?

Im not quite sure, what you mean… maybe a Dict is the solution, maybe an Array.

Lets say you want to “link an item with another one”. Then you might want to store the links in a Dict String Int and turn your List into an Array (using Array.fromList). In this case the dict will store the index of the linked item.

Or you are thinking of having different strings return the same answer, then I would use a Dict String String. then you need to find the “linkedWord” before you can use List.filter.

let
  linkedWord = 
    model.linkedWords
    |> Dict.get word
    |> Maybe.withDefault word
in
model.sentences
  |> List.filter (String.contains linkedWord)

btw. if you want to really have a “chat bot” link experience, it might be interesting to use
String.words and have a sentence as input.

That’s it from me for today, i’ll try to answer your questions properly tomorrow :wink:

1 Like

Hello @Lucas_Payr and thank you again for your time.

I would like to explain this with the image below.

Please note, that I want the “first” to be clickable!

Or, in general, Can I link the items of the first list to something else? (some options). and then when I search for a specific word on the list, only the linked option will appear, which well redirect me to the list item after clicking.

I see, well I guess what I wrote yesterday was not far off. You will definitly want to turn your headlines into a Dict.

sentenceDict : Dict String String
sentenceDict =
  [ ("Hello there this is our first sentence.","First")
  , ("this is the second sentence.","Second")
  , ("in our village its night", "Third")
  ]
  |> Dict.fromList

We also want to be able to select a headline. For this we can add a new field to our model:

type alias Model =
  { sentences : List String
  , results : List String
  , selected : Maybe String
  }

Also a new Msg Sort:

type Msg =
  WordEntered String
  | SentenceSelected String

Next we want to implement the clicking.

case msg of
  SentenceSelected string ->
    case sentenceDict |> Dict.get string of
      Just headline ->
        { model | selected = Just headline }
      Nothing ->
        -- Default behavoir
        model

I’m guessing you already know how to write the corresponding code in the view.


From what I can tell, you want to display only the corresponding headlines of sentences that contain the searched word. And once a headline is selected, we display the sentences within the headline.

div[]
  ( ( case selected of
        Just headline ->
          model.results
          |> List.filter 
            (\sentence ->
              (sentenceDict |> Dict.get sentence)
              == (Just headline)
            )
        Nothing ->
          model.results
          |> List.filterMap 
            (\sentence -> sentenceDict |> Dict.get sentence)
          |> Set.fromList --eliminate multiples by turning it into a set.
          |> Set.toList --maybe results should be a set from the very start
      )
      |> List.map rowItem
  )
1 Like

The easy answer would be:

if word == "" then
  { model | results = [] }
else
  --your old code

But generally, this is a code smell. This might be okay for your first project but in the future try to only have a “single source of truth”, meaning everything that has to do with visuals (like the computation of displayed results) should happen in the view. This might feel weird at first, because your code becomes thereby slower. But it also will be easier to maintain. Once efficiency becomes important, you can start moving parts back from the view into the model. But for the complexity of your current project, I’m guessing its fine if results is living in the view.

1 Like

That solves the Problems overall.
Thank you.
:heart_eyes:

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.