adlatitude

 

Creating custom filters with drupal and views | chadcf

Ah views. Yes, the views module for Drupal is incredibly powerful and combined with CCK has essentially eliminated the need to ever create custom modules for content. While this is a happy thing, sometimes views just doesn't quite do what you want. And, rather sadly, there is almost no documentation on how to create custom views plugins/styles/filters/etc. Boo.

For a recent project, I had the need to have a view filtered by a single dropdown menu that could contain either taxonomy terms or date parameters. While drupal supports exposed filters for both of these things, it does not support having them both in one select menu. My initial attempt was to just create a block that through some voodoo found and submitted the view with arguments for the view. This almost worked, but tended to screw up ajax pagination, and was rather fragile to begin with. So, I embarked on the path of determining how one might go about creating a truly custom filter for a view.

Step 1 - Create your module

custom_filter.module

<?php
function custom_filter_views_api() {
  return array(
   
'api' => 2,
   
'path' => drupal_get_path('module', 'custom_filter') . '/inc'
 
);
}
?>

And that's about it... All this really says is my module targets views version 2, and you can find the files in the 'inc' directory of my module folder.

Step 2 - Module Definitions

Next we need to tell views about what our module provides.

inc/custom_filter.views.inc

<?php
/**
* Implementation of hook_views_handlers() to register all of the basic handlers
* views uses.
*/
function custom_filter_views_handlers() {
  return array(
    'info' => array(
      'path' => drupal_get_path('module', 'custom_filter') . '/inc', // path to view files
    ),
    'handlers' => array(
      // register our custom filter, with the class/file name and parent class
      'custom_filters_filter_multiple' => array(
        'parent' => 'views_handler_filter',
      )
    ),
  );
}

The above definition essentially tells views what our module does. In this case we simply give it the path to our files, and register one handler named 'custom_filters_filter_multiple. The parent parameter tells views which class our handler extends. In this case, I just extended the root filter class.

Next we need to provide a views_data() hook, which tells views a bit of information about our filter.

inc/custom_filter.views.inc

function custom_filter_views_data() {
  $data = array();
 
  // The flexible date filter.
  $data['node']['custom_filter'] = array(
    'group' => t('Custom'),
    'real field'  => 'custom_filter',
    'title' => t('Custom Date/Term combined filter'),
    'help' => t('Filter any Views based on date and term'),
    'filter' => array(
      'handler' => 'custom_filters_filter_multiple'
    ),
  );
 
  return $data;
}

In this case we return an array with our filter data. We give it a group (the title of which appears in the drop down to filter, uh, filters when you add a new filter to your view). We give it a real field, which in this instance does not actually exist since we're not filtering on a single real field. The title and help are displayed in the views UI, and the handler again points to our class for implementing this filter. Views seems to like redundency...

Step 3 - Filter Definition

Now it's time to actually create our filter. We create a file which contains our class, both matching the names we provided above.

inc/custom_filters_filter_multiple.inc

<?php

class custom_filters_filter_multiple extends views_handler_filter {
 
  /* this method is used to create the options form for the Views UI when creating a view
   * we use the standard drupal form api to return a form array, with the settings
   * we want to capture.
   */
  function options_form(&$form, &$form_state) {
    parent::options_form($form, $form_state);

    // Step 1: fetch all our vocabularies, and build an array of options
    $terms = taxonomy_get_vocabularies();
    $show = array();
    foreach($terms as $term) {
      $show[$term->vid] = $term->name;
    }
   
    // Step 2: create a select field to choose Vocabulary options
    //   this allows you to choose which vocabulary to fetch terms for in the exposed filter
    $form['filter_vocab'] = array(
      '#type' => 'select',
      '#title'  => t('Vocabulary'),
      '#options'  => $show,
      '#default_value'  => $this->options['filter_vocab']
    );
   
    // Step 3: Create a checkbox field to select whether date options should be included
    $form['include_dates'] = array(
      '#type' => 'checkbox',
      '#title'  => t('Include Date Filters'),
      '#default_value'  => $this->options['include_dates']
    );
  }
 
  /* I'll be perfectly honest, I have no idea if this is required or not. I *think* it may be
   * as it defines our filter field. However I don't use it. I added it when trying to get
   * things working...
   */
  function value_form(&$form, &$form_state) {
    $form['custom_filter']  = array(
      '#type' => 'textfield'
    );
  }
 
  /* A custom display for our exposed form. Views normally uses the value_form for this
   * however we're skipping that entirely since we want our exposed form to be a completely
   * different beast. It's entirely possible I could move this to value_form however...
   */
  function exposed_form(&$form, &$form_state) {
    // for my use case the filtering is controlled by javascript, which
    // submits the form and also handles a date select popup.
    drupal_add_js(drupal_get_path('module','custom_filter') . '/custom_filter.js');
   
    // if we're displaying date options, add them to the options for our output filter
    if ( $this->options['include_dates'] ) {
      $display = array(
        'all'               => t('Show all'),
        'last-four-weeks'   => t('Last four weeks'),
        'last-three-months' => t('Last three months'),
        'last-six-months'   => t('Last six months'),
        'last-year'         => t('Last year'),
        'advanced'          => t('Advanced'),
      );   
    } else {
      $display = array('all'  => t('Show all'));
    }
   
    // get the terms for our configured vocabulary and add them to the options
    // for my case, terms are only 1 level deep, will need changes
    // if you have nested terms.
    if ( $this->options['filter_vocab'] ) {
      $terms = taxonomy_get_tree($this->options['filter_vocab']);
     
      foreach( $terms as $term ) {
        $display[$term->tid] = $term->name;
      }
    }
   
    // now create our select element with the chosen options
    $form['custom_filter'] = array(
      '#type' => 'select',
      '#title'  => t('Browse By'),
      '#options'  => $display
    );
  }

  // the query method is responsible for actually running our exposed filter
  function query() {
    // make sure our base table is included in the query.
    // base table for this is node so it may be redundent...
    $this->ensure_my_table();

    // make sure term node is joined in if needed
    // not exactly optimal since we may not need it if we're filtering by date
    $this->query->add_table('term_node');
   
    // get the value of the submitted filter
    $value = $this->value[0];

    // a bit ugly. Since we have date and taxonomy options we need to do a switch to exhaust
    // the date options before we can assume it's a taxonomy term
    switch( $value ) {
      case 'all';
        return;
      case 'last-four-weeks':
        $this->query->add_where($this->options['group'], "node.created > %s", strtotime('4 weeks ago'));
        break;
      case 'last-three-months':
        $this->query->add_where($this->options['group'], "node.created > %s", strtotime('3 months ago'));
        break;
      case 'last-six-months':
        $this->query->add_where($this->options['group'], "node.created > %s", strtotime('6 months ago'));
        break;
      case 'last-year':
        $this->query->add_where($this->options['group'], "node.created > %s", strtotime('1 year ago'));
        break;
      default:
        if ( is_numeric($value) ) {
          $this->query->add_where($this->options['group'], "term_node.tid = %d", $value);
        }
    }
  }
}

Hopefully that all makes sense. I'm not entirely sure it's the proper way to do it, but it works well and does what I need!

This is based on the Views 2 API. In Views 3 the hook_view_handlers is no longer used. Instead you register the new handler in your module .info file like so:

; Views Handlers
files[] = includes/views/handlers/archive_handler_filter_time_period.inc

Posted
 

Drupal Entities - Part 3 - Programming Hello Drupal Entity

After way too long a time I finally get back to the series of posts (check out Part I and Part II) on Entities to present the third in the series - how to actually create an entity. Part of the challenge of writing a post like this is that there are so many different aspects to entities that it is really hard to distill things into a single post that gives you something useful at the end. What we will do will be the equivalent of a "Hello World" for entities. It is not very useful in and of itself but it does get you started and introduces the main concepts.

We are going to create a single entity that has just two fields, an id and some content. For the sake of reference I will call it the PostIt entity. Imagine you wanted to create a simple PostIt system or shoutbox for your Drupal site - this would be part of the way of getting there.

First thing - we need to describe a table to Drupal which will be our "base" table - very much like the Node table, this will be where things start. This description will go into postit.install in a module directory called postit

function postit_schema() {
  $schema['postit'] = array (
    'description' => 'The main store for our entity',
    'fields' => array(
      'pid' => array(
        'description' => 'Primary key for our table of postit notes',
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'note' => array(
        'description' => 'The actual note',
        'type' => 'varchar',
        'length' => '255',
        'not null' => TRUE
      ),
    ),
    'primary key' => array('pid'),
  );
  
  return $schema;
}
All we are doing here is telling Drupal to create a table with two fields, one of which is the primary key. The table will be created as soon as the module is enabled. Ok - so we have a starting point. Next up, we need to go tell Drupal that this is the base table of our entity and set up a few things about how it should treat the entity. There is, naturally, a hook for this - aptly called hook_entity_info() and, as with most things Drupal, you construct a huge array with all the configuration information.
function postit_entity_info(){
  $postit_info['postit'] = array(
    'label' => t('PostIt Note'),
    'controller class' => 'PostItController',
    'base table' => 'postit',
    'uri callback' => 'postit_uri',
    'fieldable' => TRUE,
    'entity keys' => array(
      'id' => 'pid',
    ),
    'static cache' => TRUE,
    'bundles' => array(
      'postit'=> array(
        'label' => 'PostIt',
        'admin' => array(
          'path' => 'admin/structure/postit/manage',
          'access arguments' => array('administer postits'),
        ),
      ),
    ),
    'view modes' => array(
      'full' => array(
        'label' => t('Full PostIt'),
        'custom settings' =>  FALSE,
      ),
    )
  );
  
  return $postit_info;
}
Ok - so let us step through this. We provide a label to have a human readable reference to our entity and define a class, the PostItController class, that will be responsible for our postit. This class will actually subclass the DrupalDefaultEntityController class (which lives in entity.inc) and worries about such things such as caching, querying, attaching to fields, etc. You don't strictly need to define this class (it will automatically use the default one), but if you did want to override some of the existing functionality this would be the way to do it. The class can go in a separate file called postit.controller.inc with just this code for the time being.
class PostItController extends DrupalDefaultEntityController{}
We then let Drupal know what the base table will be for our entity and what function should be called to create URIs for this entity. The Entity API will use this function when trying to figure out URIs for entities. The next one is the biggie, our entity is fieldable which means that the Field API can now hook into our entity and we can easily extend it with fields to our hearts content. We then define what the main identifier for our entity is and we turn on caching. We now move on to the bundle part, which is really where we practically stitch together the FieldAPI with our entity. A way to think of bundles is as a type of entity (in this case a postit entity) "bundled" together with fields to forms a particular subtype. For example, different types of nodes are different bundles. The Entity API in Drupal can support multiple bundles but in our case we are just defining one and providing a URL where one can go to manage this specific bundle. Finally, we define view modes - in this case just one. And that is pretty much it. It could be a bit simpler, but it can also get much more complicated. As I mentioned this is enough for "hello entity!". Let us now move on to provide the bare minimum functionality to be able to enable our entity module, view an entity and attach fields to it. First up, taking care of the URI:
function postit_uri($postit){
  return array(
    'path' => 'postit/' . $postit->id,
  );
}
Then a couple of functions that will load our entities for us:
function postit_load($pid = NULL, $reset = FALSE){
  $pids = (isset ($pid) ? array($pid) : array());
  $postit = postit_load_multiple($pids, $reset);
  return $postit ? reset ($postit) : FALSE;
}

function postit_load_multiple($pids = array(), $conditions = array(), $reset = FALSE){
  return entity_load('postit', $pids, $conditions, $reset);
}
The first function simply takes care of whether it is going to load just one or multiple entities and the second function actually goes ahead and loads the entities calling entity_load(). The code below setups the various pages we will use to view our entities. We have a manage page that just provides an access point and link for the Field API to attach itself to our entity via the UI. If you visit "admin/structure/postit/magage" you should see tabs for adding and editing fields. The other url is where we will view our entity.
function postit_menu(){
  $items['admin/structure/postit/manage'] = array(
    'title' => 'PostIt Admin',
    'description' => 'Manage PostIT structure',
    'page callback' => 'postit_info',
    'access arguments' => array('administer postits'),
  );
  $items['postit/%postit'] = array(
    'title callback' => 'postit_page_title',
    'title arguments' => array(1),
    'page callback' => 'postit_page_view',
    'page arguments' => array(1),
    'access arguments' => array('view postits'),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

function postit_permission(){
    return array(
    'administer postits' =>  array(
      'title' => t('Administer postits'),
      'restrict access' => TRUE,
    ),
    'view postsits' => array(
      'title' => t('View PostIts'),
    )
  );
}


function postit_info() {
  return ('Welcome to the administration page for your Postits!');
}

function postit_page_title($postit){
  return $postit->pid;
}

function postit_page_view($postit, $view_mode = 'full'){
  $postit->content = array();

  // Build fields content.
  field_attach_prepare_view('postit', array($postit->pid => $postit), $view_mode);
  entity_prepare_view('postit', array($postit->pid => $postit));
  $postit->content += field_attach_view('postit', $postit, $view_mode);

  return $postit->content;
}


function postit_field_extra_fields() {
  $return = array();
  $return['postit']['postit'] = array(
    'form' => array(
      'note' => array(
        'label' => t('Note'),
        'description' => t('PostIt Note'),
      ),
    ),
  );

  return $return;
}
Now, with all these in place you are able to attach fields to the entity but there is no UI to actually create an entity. If you visit admin/structure/postit/manage you should be able to see tabs that enable you add and remove fields to your entity. We will now quickly create a basic UI to add entities so we have just enough to prove everything works together: We will add another path to hook_menu:
$items['postit/add'] = array(
    'title' => 'Add PostIT!',
    'page callback' => 'postit_add',
    'access arguments' => array('create postit'),
  );
and create the function that is called back.
function postit_add() {
  $postit = (object) array (
    'pid' => '',
    'type' => 'postit',
    'note' => '',
  );
  
  return drupal_get_form('postit_add_form', $postit);
}


function postit_add_form($form, &$form_state, $postit) {
  $form['note'] = array(
    '#type' => 'textfield',
    '#title' => t('Note'),
    '#required' => TRUE,
  );
  
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  );
  
  field_attach_form('postit', $postit, $form, $form_state);
  
  return $form;
}
What we are doing here is creating a form with the note field and then via field_attach_form we are adding any other widgets that are coming via the Field API. The form submission goes through the usual validation steps:
function postit_add_form_validate($form, &$form_state) {
  $postit_submisttion = (object) $form_state['values'];
  field_attach_form_validate('postit', $postit_submisttion, $form, $form_state);
}

function postit_add_form_submit($form, &$form_state) {
  $postit_submission = (object) $form_state['values'];
  field_attach_submit('postit', $postit_submission, $form, $form_state);
  $postit = postit_save($postit_submission);
  $form_state['redirect'] = "postit/$postit->pid";
}
The pattern here is pretty much "Take care of your own fields and then let the Field API do its own thing for the attached fields. The one function that is missing is the postit_save function which is below:
function postit_save(&$postit) {
  return entity_get_controller('postit')->save($postit);
}
For illustration sake (given that we could do the job without the controller), we delegate the work to our entity controller that has one more function added to it:
public function save($postit) {
    drupal_write_record('postit', $postit);
    field_attach_insert('postit', $postit);
    module_invoke_all('entity_insert', 'postit', $postit);
    return $postit;
  }

Similar pattern as before, we first save to our own entity table, and then save all the associated fields. Finally, give all the other modules a shout via a hook that an entity has been inserted and we are done.

Visit postit/add you will be able to create the postit and if you've attached any fields to it you will see their widgets there as well. Check the db and you will see your postit table getting updated.

There is still a bit to do, such as deleting and editing entities - offering a page to view a list of all entities, etc - but as I said this is a "Hello Entity!" example. Check out the excellent work that is being done with the Entity project for examples of a full blown CRUD controller for entities.

As you can imagine the possibilities with entities are endless and this has only touched the surface. Hopefully, examples will soon start popping up in the wild and patterns and best practices will start emerging on how to create and handle entities.

Here is a link to a module that I submitted as a patch to the Examples project on d.o. - variable names are different but the approach is the same.

For a more comprehensive and realistic example check out the Model Entities project.

How to create a Drupal Entity through code.

Posted
 

The American Scholar: Solitude and Leadership - William Deresiewicz

My title must seem like a contradiction. What can solitude have to do with leadership? Solitude means being alone, and leadership necessitates the presence of others—the people you’re leading. When we think about leadership in American history we are likely to think of Washington, at the head of an army, or Lincoln, at the head of a nation, or King, at the head of a movement—people with multitudes behind them, looking to them for direction. And when we think of solitude, we are apt to think of Thoreau, a man alone in the woods, keeping a journal and communing with nature in silence.

Leadership is what you are here to learn—the qualities of character and mind that will make you fit to command a platoon, and beyond that, perhaps, a company, a battalion, or, if you leave the military, a corporation, a foundation, a department of government. Solitude is what you have the least of here, especially as plebes. You don’t even have privacy, the opportunity simply to be physically alone, never mind solitude, the ability to be alone with your thoughts. And yet I submit to you that solitude is one of the most important necessities of true leadership. This lecture will be an attempt to explain why.

We need to begin by talking about what leadership really means. I just spent 10 years teaching at another institution that, like West Point, liked to talk a lot about leadership, Yale University. A school that some of you might have gone to had you not come here, that some of your friends might be going to. And if not Yale, then Harvard, Stanford, MIT, and so forth. These institutions, like West Point, also see their role as the training of leaders, constantly encourage their students, like West Point, to regard themselves as leaders among their peers and future leaders of society. Indeed, when we look around at the American elite, the people in charge of government, business, academia, and all our other major institutions—senators, judges, CEOs, college presidents, and so forth—we find that they come overwhelmingly either from the Ivy League and its peer institutions or from the service academies, especially West Point.

In Solitude and Leadership, William Deresiewicz argues that leaders need to spend some time alone with their thoughts and ideas so they know why and where they are leading. Delivered as a lecture at West Point, it is worth reading in its entirety.

Posted
 

How to change the default MAMP MySQL Password

When you are changing the default user/password from root/root, you need to do two things. Only the first is documented in the MAMP start page FAQ, but if you don't do the second then you get an error message when you try to access anything from the MAMP start page.

  

  1. Use command line to change the password (as documented in the MAMP start page FAQ)

    Open the terminal and type the following:
    /Applications/MAMP/Library/bin/mysqladmin -u root -p password [NEWPASSWORD]

    It will ask for the current password after you hit enter. Once you have entered that, the MySQL password is changed.

  2. Open a text editor and change the password in the three following files:
  • /Applications/MAMP/bin/phpMyAdmin-X.X.X/config.inc.php. Find the line that reads... $cfg['Servers'][$i]['password'] = 'root'; ...and change the value 'root' to your new password, retaining the quotes.
  • /Applications/MAMP/bin/mamp/index.php. Find the line that reads... $link = @mysql_connect(':/Applications/MAMP/tmp/mysql/mysql.sock', 'root', 'root'); ...and change the last parameter to your new password.
  • /Applications/MAMP/bin/stopMysql.sh Replace the -proot with your password. (If you don't change this one you can have zombie mysqld's running after you thought you stopped the server)

 

Posted
 

SASS adaptation of 960gs

So this past weekend I started working on a bare wordpress theme called NakedCompass. And as part of that project I decided that I also wanted to tie it together with the CSS Framework Compass (and SASS). Now Compass comes bundled with a SASS adaptation of the excellent Blueprint framework. But I personally prefer the 960gs framework, which unfortunately does not come with Compass. And try as I might, I could not seem to find a SASS adaptation of the 960gs framework anywhere on the web. So I created my own.

If you would like to take a look / use it, check it out on github at A SASS adaptation of the 960gs framework.

UPDATE: apparently I did not look hard enough. Here is a 960gs Compass plugin. Still it was a fun exercise in SASS.

Posted
 

Weekend Project - NakedCompass - An HTML5 Boilerplate Theme for Wordpress

So this weekend I decided to bring together Paul Irish's HTML5 Boilerplate and Wordpress. I found a few projects on the web already trying to do this... but none that really met my own requirements. That is they didn't work with the HTML5 Boilerplate build script, and they didn't make use of wordpress conventions (such as using wp_enqueue_script). The one that came the closest was by Jay George, who by the way has put together and excellent three video tutorial on how to start modifing your wordpress theme to work with HTML5 Boilerplate

So I spent the weekend hacking away and came up with NakedCompass. Download, use it, modify it... its GPL licensed. Keep in mind it is a work in progress, so if you have any suggestions I am all ears.

In any case, this is not a tutorial... just some results I would like to share. Below are some screenshots of the the before and after optimization speed for page loads. The results may not seem all that significant... but this is a completely naked theme, so seeing any kind of difference is significant and the difference will only increase as the size of the site grows.

Html-before

Before

 

Html-after

After

 

Total-before

Before

 

Total-after

After

Posted
 

Sublime Text - An Awesome Text Editor

I just discoverd Sublime Text and in my opinion it is the best text editor that I have used yet. Lacks some of the code intelligence that Coda offers, and the plugins that are available for TextMate. But makes up for it by letting you split your screen into columns, the 10000 mile view, and many other goodies. It's free to trial so just go and check it out yourself.

Posted
 

SEO Friendly Select Menu Using CSS and jQuery

The scenario: For a project I am working on I wanted an ajax powered navigation menu that worked exactly like a dropdown select box nav menu… i.e. first click opens the dropdown, next click selects the item (link) you want to go to, the dropdown closes, and the link you just selected is now displayed in the ‘select’ box. Doing this using a select box is pretty easy.

So why not just use the select box? Well… initially I did. But then it hit me. I didn’t know whether or not this method was SEO friendly. My gut said no because the select box value is not a link, and after some searching (‘select box navigation seo’) my initial conclusion seemed to be correct. It is not SEO friendly. So I decided to use a bit of CSS and jQuery / Javascript to mimic the functionality that I wanted but still preserve the links for the spiders.

In a nutshell I used absolute positioning, and hidden overflow for the wrapper container so that only the selected item was visible while the dropdown was closed.

The HTML:

<div class="filter">
    <span> Filter by </span>
    <ul>
      <li> <a href="link1" > option1 </a> </li>
      <li> <a href="link2" > option2 </a> </li>
      <li> <a href="link3" > option3 </a> </li>
      <li> <a href="link4" > option4 </a> </li>
    </ul>  
</div>

The CSS:

/* The wrapper */
.filter {
    font-size: 13px;
    width: 250px;
    margin: 0px;
    padding: 0px;
    border: none;
    background: #fff;
    position: relative; /* this is important */
    overflow: hidden; /* this is important */
}

    /* The label */
    .filter span {
        display: block;
        width: 50px;
        background: #002B36;
        text-align: center;
        padding: 5px 10px;
        font-weight: bold;
        color: #DEE4EA;
    }

    /* The dropdown */
    .filter ul {
        position: absolute; /* this is important */
        top: 0px;
        right: 15px;
        width: 165px;
        background: #fff;
    }

    .filter ul li {
        display: block;
        font-weight: normal;
        padding: 5px 10px;
    }

    .filter ul li a {
        display: block;
    }

The jQuery/Javascript:

/* The selectBox function */
jQuery.fn.selectBox = function() {
var o = jQuery(this[0]); // The dropdown wrapper
var dropdown = o.children('ul'); // The dropdown
var item = dropdown.children('li');
var height = item.outerHeight();

item.each(function(){
    jQuery(this).click(function(e){
        e.preventDefault();
        e.stopPropagation();
        var isOpen = o.css('overflow') == 'visible';
        var path = jQuery(this).children('a').attr('href');
        var index = jQuery(this).index();
        var top = (index*height);
        var show = isOpen ? 'hidden' : 'visible';
        o.css({ 'overflow' : show });
        dropdown.css({ 'top' : -top }); // move the dropdown so that only the selected option is displayed

        // If closed, the first click is to open the dropdown... not a selection
        if (isOpen){
            jQuery(this).siblings().removeClass('active');
            jQuery(this).addClass('active');
            jQuery('.container #main_content').load(path); // The div in which to load the new content
        }
    });
}); 

// If a click outside the filter occurs... then hide the dropdown
jQuery('body').click(function() {
    if (o.css('overflow') == 'visible') {
        o.css({
            'overflow' : 'hidden'
        });
    }
});
}

/* Call it on document.ready() */
jQuery(function() {
jQuery('.filter').selectBox();
});

p.s. On a sidenote… (this is a rant) why the hell is html example code so damn difficult to put into a post? One should not have to escape the html characters manually in order for it to display correctly.

Posted
 

Programming Praxis

Today’s problem appears with some regularity at places like Proggit and Stack Overflow and in lists of programming interview questions:

Given a binary tree t and two elements of the tree, m and n, with m<n, find the lowest element of the tree (farthest from the root) that is an ancestor of both m and n.

For example, in the tree shown at right, the lowest common ancestor of 4 and 7 is 6, the lowest common ancestor of 4 and 10 is 8, and the lowest common ancestor of 1 and 4 is 3. It is possible for an element of the tree to be its own ancestor, so the lowest common ancestor of 1 and 3 is 3, and the lowest common ancestor of 3 and 6 is 3.

Your task is to write a function that finds the lowest common ancestor of two elements of a binary tree. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.

Pages: 1 2

Weekly challenges to keep your programming skills and brain from falling into a stuck-in-the-box trap. Who knows... maybe one of these problems will inspire you.

Posted
 

Legos at work

(download)

Proudly introducing the first ever Lego sculpture built at Enswers.

Architecture and design by Andrii, Johnny, and myself.

We call it... The Deceptive Cabin... complete with swinging doors, draw bridge, death ray, dune buggy, and dismembered individual. This is one cabin you don't want to mess with.

Posted
Posterous theme by Cory Watilo.