1

I have structured data, with various types of data in it. For the sake of simplicity, let's say I have something like this:

{
  person : [
    { 
      name : 'paul',
      title : 'prof',
      profession : 'teacher',
      start: '2010-10-10'
    },
    { 
      name : 'joe',
      title : 'dr',
      profession : 'scientist',
      start: '2000-01-01'
    }
  ]
  book : [
    {
       title : 'the cat in the hat'
    }
  ] 
}

I would like to have an autocomplete box in javascript that lets me choose the names of these structured elements so the following results would be returned given the letter typed:

't' : {'person.title', 'book.title'}
'p' : {'person', 'person.profession'}

What is important to me is that the person might know the name of any of the variables within the tree. So if they type in the name of the top level variable, I only want to show that one and none of it's sub-elements, but if they type in the name of sub-element, I want the full path to that sub-element displayed. If they type in a top-level variable ("person"), I don't want to display all the sub-elements, only ones that always begin with that same set of letters.

Are there any libraries out there that can currently do this (provide a way of doing auto-complete on structured data) as opposed to normal auto-complete?

Clarification: I guess what I need is the ability to tell the auto-complete library a map of inputs and outputs to work with, such that typing "p" will end up hitting on the inputs "person" and "profession" and thus return "person" and "person.profession", and typing "t" hits on "title" for "person.title" and "title" for "book.title".

0xdabbad00
  • 998
  • 2
  • 11
  • 22

3 Answers3

1

Take a look at the autocomplete jQuery-ui functionality : http://jqueryui.com/autocomplete/

You'll need to use the source parameter. Just read the api documentation.

Magus
  • 14,796
  • 3
  • 36
  • 51
1

Just wrote a function that recurses over an object to retrieve the fullpath property names as an array e.g. person.title. The array can then be used with jQueryUI autocomplete functionality. Please look at the fiddle to confirm this is what you wanted.

Fiddle here

var retrieveUniqueProps = ( function ( ) {

    var result = [];
    var added = {};

    isArray = function( o ) { 
        return Object.prototype.toString.call( o ) === "[object Array]";
    };

    isObject = function( o ) { return typeof o === "object"; };

    return function ( obj, parentPath ) {

        if( isArray( obj ) ) {

            for( var i = 0; i < obj.length; i++ ) {

                if( isArray( obj[i] ) || isObject( obj[i] ) ){ 
                    retrieveUniqueProps( obj[i], parentPath ); 
                }
            }

        } else if ( isObject( obj ) ) {

            for( var a in obj ) {

                if( obj.hasOwnProperty( a ) ) {

                    var fullpath = parentPath ? parentPath + "." + a : a;
                    if( !added[ fullpath ] ) {
                        result.push( fullpath );
                        added[ fullpath ] = true;
                    }

                    if( isArray( obj[a] ) || isObject( obj[a] ) ){ 
                        retrieveUniqueProps( obj[a], parentPath ? parentPath + "." + a : a ); 
                    }
                }
            }
        }

        return result;
    };


}());

var uniquePropertyNames = retrieveUniqueProps( o, "" );

UPDATE I have amended the source option of autocomplete to filter out the results as per your requirements. The last word must match what you have typed into the input. Check out the fiddle for the updated version.

 $("#props").autocomplete({
    source: function(request, response) {

                // The term the user searched for;
                var term = request.term;

                // Extract matching items:
                var matches = $.grep(uniquePropertyNames, function(item, index) {
                // Build your regex here:
                var subArray = item.split( "." );

                if( subArray[subArray.length - 1].indexOf( term ) !== 0 ) return false;
                return true;
            });

            // let autocomplete know the results:
            response(matches);        
        }
});
Bruno
  • 5,772
  • 1
  • 26
  • 43
  • This is the default functionality of autocomplete, and not what I want. My desired outcome differs in 2 main ways from your solution: 1) When I type 'p', I should only see words that start with that letter so not "anotherProp" (likely an easy fix via some parameter change). 2) I only want to see the results "person" and "person.profession", not "person.title". – 0xdabbad00 Nov 15 '12 at 14:56
  • @0xdabbad00 can I just clarify. So you want to return all entries whose last word starts with a p because the example you gave was type t return _person.title_ and _book.title_. – Bruno Nov 16 '12 at 10:34
  • That's correct. Your fix worked. You rock! Thank you. This work was for the site icebuddha.com, I'll make sure to credit you on there. – 0xdabbad00 Nov 16 '12 at 13:37
-1

You can use the Object.getOwnProperty method to read all the keys of an object and run an autocomplete over the results.

Some discussions here - How to get an object's properties in JavaScript / jQuery?

Community
  • 1
  • 1
web-nomad
  • 6,003
  • 3
  • 34
  • 49
  • You mean `Object.getOwnPropertyNames`. However, `Object.keys` is better. Compare the output of `Object.keys([ 1, 2, 3 ])` and `Object.getOwnPropertyNames([ 1, 2, 3 ])`. – katspaugh Nov 15 '12 at 08:29