Custom Selectables#

As described in the previous section Selectables are the tidychef classes that provide the cell selection mechanisms.

Some common selecrables are shown below.

Type

Description

tidychef.selection.selectable.Selectable

The common class all selectables inherit from. This is where the selection methods all tabulated sources have in common reside

tidychef.selection.csv.csv.CsvSelectable

Extends Selectable so we can add methods that only make sense in the context of processing csv source data

tidychef.selection.xls.xls.XlsSelectable

Extends Selectable so we can add methods that only make sense in the context of processing xls source data

tidychef.selection.xlsx.xlsx.XlsxSelectable

Extends Selectable so we can add methods that only make sense in the context of processing xlsx source data

This document will explain how to ceate your own custom selectables with methods of your own devising.

Source Data#

The data source we’re using for these examples is shown below:

The full data source can be viewed here.

from tidychef import acquire, preview
from tidychef.selection import CsvSelectable

table: CsvSelectable = acquire.csv.http("https://raw.githubusercontent.com/mikeAdamss/tidychef/main/tests/fixtures/csv/bands-wide.csv")
preview(table)

Unnamed Table

ABCDEFGHIJK
1
2HousesCarsBoatsHousesCarsBoats
3BeatlesRolling Stones
4John159Keith2610
5Paul2610Mick3711
6George2711Charlie3812
7Ringo4812Ronnie5913
8

Example Scenario 1#

  • We want to create our very own custom selectable to work with csv sources such as the above.

  • We want to create a new selection method column_c() that explicitly selects cells in column C.

First we’ll create a new class inheriting from Selectable which we’ll call AwesomeNewSelectable.

from tidychef.selection.selectable import Selectable
from tidychef.utils.decorators import dontmutate

class AwesomeNewSelectable(Selectable):

    @dontmutate
    def column_c(self):
        """
        Filter down the currently selected cells to only contain
        cells that happen to be in column C
        """
        # Column C is x offset 2
        self.cells = [cell for cell in self.cells if cell.x == 2]
        return self

Three key rules are on show above that you need to adhere to it creating your own Selectables:

    1. A new selectable class should always inherit from Selectable or something that itself inherits from Selectable (eg XlsxSelectable).

    1. A Selectable method must always have a @dontmutate decorator.

    1. A selection method must always return self (that’s how the extended dot notation works).

Now lets see our new AwesomeNewSelectable and column_c method in action:

from tidychef import acquire, preview

table: AwesomeNewSelectable = acquire.csv.http(
    "https://raw.githubusercontent.com/mikeAdamss/tidychef/main/tests/fixtures/csv/bands-wide.csv",
    selectable=AwesomeNewSelectable)

# Lets just use the new method then preview it
example1 = table.column_c().label_as("Just Column C")
preview(example1)

# And together with another method to confirm command chaining is working as expected
example1 = table.column_c().is_not_blank().label_as("Non blanks in column C")
preview(example1)
Just Column C

Unnamed Table

ABCDEFGHIJK
1
2HousesCarsBoatsHousesCarsBoats
3BeatlesRolling Stones
4John159Keith2610
5Paul2610Mick3711
6George2711Charlie3812
7Ringo4812Ronnie5913
8

Non blanks in column C

Unnamed Table

ABCDEFGHIJK
1
2HousesCarsBoatsHousesCarsBoats
3BeatlesRolling Stones
4John159Keith2610
5Paul2610Mick3711
6George2711Charlie3812
7Ringo4812Ronnie5913
8

Example Scenario 2#

  • We want to create another method for AwesomeNewSelectable called grab_right() that adds every cell that is dirctly to the right of a selected cell to the current selection.

But first we need to understand about .cells vs .pcells.

.cells vs .pcells#

When writing methods for a Selectable class there are two key attributes to be aware of:

Attribute

Description

cells

The currently selected cells.

pcells

The prisine cell selection, i.e a list of cells representing the full unfiltered tabular source.

The principle is to filter down you just filter cells, but to expand cells you need to compare your cells to pcells.

An example follows:

Example 2#

from tidychef.selection.selectable import Selectable
from tidychef.utils.decorators import dontmutate

class AwesomeNewSelectable(Selectable):

    @dontmutate
    def column_c(self):
        """
        Filter down the currently selected cells to only contain
        cells that happen to be in column C
        """
        # Column C is x offset 2
        self.cells = [cell for cell in self.cells if cell.x == 2]
        return self
    
    @dontmutate
    def grab_right(self):
        """
        Adds every cell that is one cell directly to the right of
        a currently selected cell to the selection of cell.
        """
        # x is the horizontal axis, so x+1 is "one cell right"
        additional_cells = []
        for cell in self.cells:
            additional = [pcell for pcell in self.pcells if
                               cell.x+1 == pcell.x and cell.y == pcell.y]
            if additional not in self.cells:
                additional_cells.append(additional[0])

        self.cells += additional_cells
        return self
        

and now lets try it

from tidychef import acquire, preview

table: AwesomeNewSelectable = acquire.csv.http(
    "https://raw.githubusercontent.com/mikeAdamss/tidychef/main/tests/fixtures/csv/bands-wide.csv",
    selectable=AwesomeNewSelectable)

# Lets just use the new method then preview it
example1 = table.column_c().grab_right().label_as("column_c() selected with grab_right() to also get column D")
preview(example1)
column_c() selected with grab_right() to also get column D

Unnamed Table

ABCDEFGHIJK
1
2HousesCarsBoatsHousesCarsBoats
3BeatlesRolling Stones
4John159Keith2610
5Paul2610Mick3711
6George2711Charlie3812
7Ringo4812Ronnie5913
8