Skip to content

Instantly share code, notes, and snippets.

@nessup
Last active February 4, 2018 23:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nessup/23afb02dc6a5b1385270e4a9f1629282 to your computer and use it in GitHub Desktop.
Save nessup/23afb02dc6a5b1385270e4a9f1629282 to your computer and use it in GitHub Desktop.
var Joi = require('joi');
/*
* Test listings
*/
let listings = [
{
'id':4522,
'listing_price_cents':33700000,
'bedrooms':3,
'bathrooms':2.0,
'square_footage':2109,
'published':true,
'year_built':2002,
'lonlat':'POINT (-96.858753 33.116399)',
'pool_type':[
],
'parking_type':[
'garage_attached'
]
},
{
'id':4528,
'listing_price_cents':28200000,
'bedrooms':1,
'bathrooms':2.0,
'square_footage':1579,
'published':false,
'year_built':1973,
'lonlat':'POINT (-112.021649 33.606579)',
'pool_type':[
'private',
'public'
],
'parking_type':[
'garage_attached'
]
}
];
/*
* Listing filter
*/
let ListingFilter = {
/* Assumes array schema is correct */
filter: function(listings, constraints) {
ListingFilter.validateConstraints(constraints);
return listings.filter(listing => {
var result = true;
constraints.forEach(constraint => {
let value = listing[constraint.attribute];
if (constraint.minValue !== undefined) {
result = result && ListingFilter.minValue(value, constraint.minValue);
}
if (constraint.maxValue !== undefined) {
result = result && ListingFilter.maxValue(value, constraint.maxValue);
}
if (constraint.equalToValue !== undefined) {
result = result && ListingFilter.equalToValue(value, constraint.equalToValue);
}
if (constraint.containsOneOf !== undefined) {
result = result && ListingFilter.containsOneOf(value, constraint.containsOneOf);
}
if (constraint.containsAllOf !== undefined) {
result = result && ListingFilter.containsAllOf(value, constraint.containsAllOf);
}
if (constraint.withinArea !== undefined) {
result = result && ListingFilter.withinArea(value, constraint.withinArea);
}
});
return result;
});
},
validateConstraints: function(constraints) {
let constraintSchema = Joi.array().items(Joi.object().keys({
attribute: Joi.string().required(),
minValue: Joi.number(),
maxValue: Joi.number(),
equalToValue: [Joi.number(), Joi.string(), Joi.boolean()],
containsOneOf: Joi.array().items(Joi.string()),
containsAllOf: Joi.array().items(Joi.string()),
withinArea: Joi.object().keys({
lon: Joi.number(),
lat: Joi.number(),
radius: Joi.number()
})
}));
let {error} = Joi.validate(constraints, constraintSchema);
if (error) {
throw new Error(error);
}
},
minValue: function(value, constraintValue) {
return value >= constraintValue;
},
maxValue: function(value, constraintValue) {
return value <= constraintValue;
},
equalToValue: function(value, constraintValue) {
return value === constraintValue;
},
containsOneOf: function(array, items) {
let intersection = Utility.intersectArrays(array, items);
return intersection.length > 0;
},
containsAllOf: function(array, items) {
let intersection = Utility.intersectArrays(array, items);
return intersection.length === array.length;
},
withinArea: function(lonLatString, area) {
let lonLat = Utility.lonLatStringToCoordinatePair(lonLatString);
let withinArea = Utility.isCoordinatePairWithinArea(lonLat, area);
return withinArea;
}
};
/*
* Utility functions
*/
let Utility = {
intersectArrays: function(array1, array2) {
return array1.filter(item => {
return array2.indexOf(item) !== -1;
});
},
/* Converts "POINT (-96.858753 33.116399)" to {lon: -96.858753, lat: 33.116399} */
lonLatStringToCoordinatePair: function(lonLatString) {
let pair = lonLatString.substr(7, lonLatString.length - 8).split(' ');
return {
lon: pair[0],
lat: pair[1]
};
},
/* areaRadius is in km. Based on https://stackoverflow.com/revisions/24680708/2 */
isCoordinatePairWithinArea: function(coordinatePair, area) {
let ky = 40000 / 360;
let kx = Math.cos(Math.PI * area.lat / 180.0) * ky;
let dx = Math.abs(area.lon - coordinatePair.lon) * kx;
let dy = Math.abs(area.lat - coordinatePair.lat) * ky;
let distance = Math.sqrt(dx * dx + dy * dy);
return distance <= area.radius;
}
};
/*
* Main
*/
try {
let filtered = ListingFilter.filter(listings, [
{
attribute: 'bedrooms',
minValue: 0
},
{
attribute: 'bedrooms',
maxValue: 1
},
{
attribute: 'bedrooms',
equalToValue: 1
},
{
attribute: 'published',
equalToValue: false
},
{
attribute: 'pool_type',
containsOneOf: ['private']
},
{
attribute: 'pool_type',
containsAllOf: ['private', 'public']
},
{
attribute: 'lonlat',
withinArea: {
lon: -112.021649,
lat: 33.606579,
radius: 1
}
},
]);
console.log('filtered', filtered);
}
catch(error) {
console.error(error);
process.exit(1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment