0
votes

Lets say I have got this data to push into crossfilter:

[
    {user: 'abby', town: 'reading', postcode: 'RG'},
    {user: 'ben', town: 'reading', postcode: 'RG'},
    {user: 'charlie', town: 'bristol', postcode:'BS'}
]

Note the flattened nature of the the data - the postcode field will always be a mapping from the town field, so whenever town = reading, postcode will = RG etc.

I want to be able to create a 'town' dimension that is sourced from both the town and the postcode fields, so that when I filter on my user dimension, say with 'abby', then the town dimension, when I call group().all(), will return an array like:

[
    {town: 'reading', postcode: 'RG' }
]

What is the correct way to do this?

I know that I could produce a town dimension with something like

var townDimension = myCrossFilter.dimension(function(row) {
    return row.town + ':' + row.postcode;
})

And then, after calling group().all() on the filtered dimension, I would have to manually convert the list of resulting strings into a list of {town, postcode} objects.

But is there support within crossfilter for doing this?

1
It's not really worth doing this in Crossfilter, I wouldn't think. I'd just keep a map on the side that links towns and postcodes and then add the postcode when I display the data. If you must do this, you might want to investigate whether defining your dimension as a 2-element array of ["town", "postcode"] will work for you.Ethan Jewett
Yes, my aim for this question was indeed to investigate whether it is possible to define a dimension as a 2-element array of town & postcode.Simon Green
Have you tried it? I believe it should work. Though I think you'll have to use functions to filter.Ethan Jewett
Sorry, I'm not followig you. Have I tried what? If you're referring to your lookup suggestion, no I haven't - I was hoping I could use crossfilter to basically produce the filtered lookup for me when I call group().all() on the dimension. However, I'm now also wondering how I would set a filter on this town dimension too - if I pass an object into the dimension.filter() method, will it need to be the exact instance of the item in the dimension, or will it just filter by matching on the return from valueOf()Simon Green
Have you tried it with 2-element arrays? var dim = myCrossFilter.dimension(function(row) { return [row.town, row.postcode];}) for example. To filter you'll have to use functions: dim.filter(function(d) { return d[0] === 'reading'; }). The downside of doing it this way is that filtering based on functions has worse performance than defining your dimension to be just the town name and then using the more specific types of filters.Ethan Jewett

1 Answers

0
votes

As per Ethan's comment, the following code worked to create the dimension:

var dim = myCrossFilter.dimension(function(row) { return new KeyValue(row.town, row.postcode);});

In order for it to work and filter correctly, I overrode the valueOf method in my KeyValue object.

I should have mentioned that I'm actually writing this in Typescript, not Javascript, so my TypeScript code for the KeyValue class is:

class KeyValue {
    constructor(key: any, value: any) {
        this.key = key;
        this.value = value;
    }

    key: any;
    value: any;

    valueOf(): any {
        return this.key == null ? null : this.key.valueOf();
    }
}

I'm not sure if this has much of a performance impact - I'm not filtering based on a function, I can still filter using code like:

var options : KeyValue[] = dim.group().all();
var selectedOption : KeyValue = options[3]; //let's say I selected the 4th option
dim.filter(selectedOption);