How to Write Code to Autocomplete Words and Sentences

How to write code to autocomplete words and sentences?

(I'm aware this isn't exactly what you're asking for, but) If you're happy with the auto-completion/suggestions appearing on TAB (as used in many shells), then you can quickly get up and running using the readline module.

Here's a quick example based on Doug Hellmann's PyMOTW writeup on readline.

import readline

class MyCompleter(object): # Custom completer

def __init__(self, options):
self.options = sorted(options)

def complete(self, text, state):
if state == 0: # on first trigger, build possible matches
if text: # cache matches (entries that start with entered text)
self.matches = [s for s in self.options
if s and s.startswith(text)]
else: # no text entered, all matches possible
self.matches = self.options[:]

# return match indexed by state
try:
return self.matches[state]
except IndexError:
return None

completer = MyCompleter(["hello", "hi", "how are you", "goodbye", "great"])
readline.set_completer(completer.complete)
readline.parse_and_bind('tab: complete')

input = raw_input("Input: ")
print "You entered", input

This results in the following behaviour (<TAB> representing a the tab key being pressed):

Input: <TAB><TAB>
goodbye great hello hi how are you

Input: h<TAB><TAB>
hello hi how are you

Input: ho<TAB>ow are you

In the last line (HOTAB entered), there is only one possible match and the whole sentence "how are you" is auto completed.

Check out the linked articles for more information on readline.


"And better yet would be if it would complete words not only from the beginning ... completion from arbitrary part of the string."

This can be achieved by simply modifying the match criteria in the completer function, ie. from:

self.matches = [s for s in self.options 
if s and s.startswith(text)]

to something like:

self.matches = [s for s in self.options 
if text in s]

This will give you the following behaviour:

Input: <TAB><TAB>
goodbye great hello hi how are you

Input: o<TAB><TAB>
goodbye hello how are you

Updates: using the history buffer (as mentioned in comments)

A simple way to create a pseudo-menu for scrolling/searching is to load the keywords into the history buffer. You will then be able to scroll through the entries using the up/down arrow keys as well as use Ctrl+R to perform a reverse-search.

To try this out, make the following changes:

keywords = ["hello", "hi", "how are you", "goodbye", "great"]
completer = MyCompleter(keywords)
readline.set_completer(completer.complete)
readline.parse_and_bind('tab: complete')
for kw in keywords:
readline.add_history(kw)

input = raw_input("Input: ")
print "You entered", input

When you run the script, try typing Ctrl+r followed by a. That will return the first match that contains "a". Enter Ctrl+r again for the next match. To select an entry, press ENTER.

Also try using the UP/DOWN keys to scroll through the keywords.

Sentence Auto-Complete with Java

If you've only got 1000 sentences, you probably don't need a powerful indexer like lucene. I'm not sure whether you want to do "complete the sentence" suggestions or "suggest other queries that have the same keywords" suggestions. Here are solutions to both:

Assuming that you want to complete the sentence input by the user, then you could put all of your strings into a SortedSet, and use the tailSet method to get a list of strings that are "greater" than the input string (since the string comparator considers a longer string A that starts with string B to be "greater" than B). Then, iterate over the top few entries of the set returned by tailSet to create a set of strings where the first inputString.length() characters match the input string. You can stop iterating as soon as the first inputString.length() characters don't match the input string.

If you want to do keyword suggestions instead of "complete the sentence" suggestions, then the overhead depends on how long your sentences are, and how many unique words there are in the sentences. If this set is small enough, you'll be able to get away with a HashMap<String,Set<String>>, where you mapped keywords to the sentences that contained them. Then you could handle multiword queries by intersecting the sets.

In both cases, I'd probably convert all strings to lower case first (assuming that's appropriate in your application). I don't think either solution would scale to hundreds of thousands of suggestions either. Do either of those do what you want? Happy to provide code if you'd like it.

Combination of word as a autocomplete suggest once that word is selected

You can add any words to the autocomplete suggestion by pushing them to the suggestion array (a in your code). So what you would need to do is:

  1. Check if the last character is a blank space.
  2. If it is, get the last word from the #parameter input.
  3. Search that word in the list of words that you use as source (docWords).
  4. If it is in the source, add the next word to the suggestion.

A simple demo would be something like this (that would be added to your function before the responseFn( a ); part):

// get the value in the input
var textValue = $("#parameter").val();

// if the last character is a space
if (textValue.slice(-1) == " ") {

// get the last word in the sentence
var start = textValue.lastIndexOf(" ", textValue.length-2);
var lastWord = textValue.substring(start + 1, textValue.length-1);

// check if the word is in the source list
var pos = docWords.indexOf(lastWord);
if (lastWord != " " && docWords.length > pos) {

// if it is, suggest the next word too as a sentence
a.push($("#parameter").val() +docWords[pos+1]);
}
}

Notice that this is really basic example, it doesn't check for word duplicates, or case, or anything else. You would need to extend it to make it more "complete" and adjust it to your needs.


Here is a running demo:

var curDocParaText = $('.docTextFull').text();var docWords = curDocParaText.replace(/\s{2,}/g, ' ').split(" ");$( "#parameter" ).autocomplete({    source: function(req, responseFn) {        var re = $.ui.autocomplete.escapeRegex(req.term);        var matcher = new RegExp( "^" + re, "i" );        var a = $.grep( docWords, function(item,index){            return matcher.test(item);        });                        var textValue = $("#parameter").val();        if (textValue.slice(-1) == " ") {            var start = textValue.lastIndexOf(" ", textValue.length-2);            var lastWord = textValue.substring(start + 1, textValue.length-1);            var pos = docWords.indexOf(lastWord);            if (lastWord != " " && docWords.length > pos) {                a.push($("#parameter").val() +docWords[pos+1]);            }        }                responseFn( a );    }});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><script type="text/javascript" src="http://code.jquery.com/ui/1.9.2/jquery-ui.js"></script><link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.9.2/themes/base/jquery-ui.css">
<div><b>Demo Sentence:</b></div><div class="docTextFull">"The quick brown fox jumps over the lazy dog"</div><hr/><input type="text" id="parameter">

How to make a python, command-line program autocomplete arbitrary things NOT interpreter

Use Python's readline bindings. For example,

import readline

def completer(text, state):
options = [i for i in commands if i.startswith(text)]
if state < len(options):
return options[state]
else:
return None

readline.parse_and_bind("tab: complete")
readline.set_completer(completer)

The official module docs aren't much more detailed, see the readline docs for more info.

React - Material-ui : Autocomplete on words that aren't the first in a sentence

Here is how you can do it with the material-ui components:

<div style={{position:'relative'}}>

<AutoComplete
hintText="Type anything"
dataSource={['#hello','#how','#are','#you']}
ref={ref=>this.autocompleteRef=ref}
searchText={this.state.searchText}
onNewRequest={this.onNewRequest}
textFieldStyle={{visibility:'hidden'}}
style={{position:'absolute',top:'0',left:'0'}}
/>
<TextField
value={this.state.fullText || ''}
hintText="fullText"
onChange={this.myTextChange}
type="text"
/>
</div>

And the functions should look something like:

myTextChange=(event)=>{
let fullText = event.target.value,searchText;
let hashTaggingIndex = this.hashTaggingIndex;
if(hashTaggingIndex && hashTaggingIndex>-1){
if(fullText[fullText.length-1]===' ') this.hashTaggingIndex = -1;
else {
searchText = fullText.substring(hashTaggingIndex+1,fullText.length);
}
} else if(fullText[fullText.length-1]==='#') {
this.hashTaggingIndex=fullText.length-1;
}
this.setState({fullText,searchText});
if(this.autocompleteRef && hashTaggingIndex && hashTaggingIndex>-1){
this.autocompleteRef.handleChange(event);
}
};

onNewRequest=(value)=>{
let fullText=this.state.fullText;
fullText = fullText.substring(0,this.hashTaggingIndex) + value;
this.setState({fullText})
};

Have fun:)



Related Topics



Leave a reply



Submit