├── .gitignore ├── default ├── custom.js ├── index.js ├── toInt.js ├── removeProp.js ├── toString.js ├── renameCollection.js ├── replace.js ├── timeDateConvert.js ├── tofloat.js ├── sort.js ├── merge.js ├── renameProperty.js ├── split.js ├── remove.js ├── currencyConvert.js ├── Util.js └── test.js ├── package.json ├── advanced ├── rsscombine.js └── link.js ├── index.js ├── filterfuncExamples.txt ├── README.md └── test └── test_default_transforms.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | node_modules/ 3 | node-debug.log 4 | -------------------------------------------------------------------------------- /default/custom.js: -------------------------------------------------------------------------------- 1 | module.exports = function(fn) { 2 | this.results = fn.call(this, this.results); 3 | }; 4 | -------------------------------------------------------------------------------- /default/index.js: -------------------------------------------------------------------------------- 1 | [ 2 | 'sort', 3 | 'split', 4 | 'merge', 5 | 'remove', 6 | 'replace', 7 | 'removeProp', 8 | 'toInt', 9 | 'toFloat', 10 | 'renameProperty', 11 | 'renameCollection', 12 | 'currencyConvert', 13 | 'custom' 14 | ].forEach(function(filter, idx, arr) { 15 | this[filter] = require('./' + filter + '.js'); 16 | }, module.exports); 17 | -------------------------------------------------------------------------------- /default/toInt.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var Util = require('./Util.js'); 3 | 4 | module.exports = function(results, option) { 5 | var collection = option.collection; 6 | var property = option.property; 7 | 8 | results[collection] = _.map(results[collection], function(e) { 9 | Util.setPropByString(e, property, parseInt(Util.getPropByString(e, property))); 10 | return e; 11 | }); 12 | }; 13 | -------------------------------------------------------------------------------- /default/removeProp.js: -------------------------------------------------------------------------------- 1 | var Util = require('./Util.js'); 2 | var _ = require('lodash'); 3 | 4 | module.exports = function(results, option) { 5 | var collection = option.collection; 6 | var properties = option.properties; 7 | var list = results[collection]; 8 | 9 | for (var i = 0, len = list.length; i < len; i++) { 10 | _.forEach(properties, function(prop, idx) { 11 | Util.deletePropByString(list[i], prop); 12 | }); 13 | } 14 | }; 15 | 16 | 17 | -------------------------------------------------------------------------------- /default/toString.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var Util = require('./Util.js'); 3 | 4 | module.exports = function(results, option) { 5 | var results = this.results; 6 | var collection = option.collection; 7 | var property = option.property; 8 | var fn = option.fn; 9 | 10 | results[collection] = _.map(results[collection], function(e) { 11 | Util.setPropByString(e, property, fn(Util.getPropByString(e, property))); 12 | return e; 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /default/renameCollection.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var Util = require('./Util.js'); 3 | 4 | module.exports = function(results, option) { 5 | var collection = option.collection; 6 | var newname = option.newname; 7 | 8 | // invalid collection 9 | if(results[collection] === undefined) return; 10 | 11 | results[newname] = _.cloneDeep(results[collection]); 12 | delete results[collection]; 13 | 14 | // TODO: fix it 15 | // update current collection if renamed 16 | if(this.currentCollection === collection) 17 | this.setCurrCollection(newname); 18 | }; 19 | -------------------------------------------------------------------------------- /default/replace.js: -------------------------------------------------------------------------------- 1 | var Util = require('./Util.js'); 2 | var Q = require('q'); 3 | 4 | module.exports = function(results, option) { 5 | var collection = option.collection; 6 | var property = option.property; 7 | var from = option.from; 8 | var to = option.to; 9 | 10 | // process results 11 | var list = results[collection]; 12 | for (var i = 0, len = list.length; i < len; i++) { 13 | try { 14 | Util.setPropByString(list[i], property, Util.getPropByString(list[i], property).replace(from, to)); 15 | } catch(err) { 16 | throw err; 17 | }; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /default/timeDateConvert.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var moment = require('moment'); 3 | var Util = require('./Util.js'); 4 | 5 | module.exports = function(results, option) { 6 | var collection = option.collection; 7 | var property = option.property; 8 | var fromFormat = option.fromFormat; 9 | var toFormat = option.toFormat; 10 | 11 | _.forEach(results, function(e) { 12 | var oldval = Util.getPropByString(e, properties); 13 | var newval = moment(oldval, fromFormat).format(toFormat); 14 | Util.setPropByString(e, property, newval); 15 | }); 16 | }; 17 | 18 | -------------------------------------------------------------------------------- /default/tofloat.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var Util = require('./Util.js'); 3 | 4 | module.exports = function(results, option) { 5 | var collection = option.collection; 6 | var property = option.property; 7 | var decimal = option.decimal; 8 | 9 | results[collection] = _.map(results[collection], function(e) { 10 | var val = decimal === undefined 11 | ? parseFloat(Util.getPropByString(e, property)) 12 | : parseFloat(parseFloat(Util.getPropByString(e, property)).toFixed(decimal)); 13 | 14 | Util.setPropByString(e, property, val); 15 | return e; 16 | }); 17 | }; 18 | -------------------------------------------------------------------------------- /default/sort.js: -------------------------------------------------------------------------------- 1 | var Util = require('./Util.js'); 2 | 3 | module.exports = function(results, option) { 4 | var collection = option.collection; 5 | var property = option.property; 6 | var lowToHigh = option.lowToHigh === undefined ? 1 : option.lowToHigh; 7 | 8 | results[collection].sort(function(a, b) { 9 | var valA = Util.getPropByString(a, property); 10 | var valB = Util.getPropByString(b, property); 11 | 12 | if(valA < valB) return -1; 13 | else if(valA === valB) return 0; 14 | else return 1; 15 | }); 16 | 17 | if(!lowToHigh) { 18 | results[collection].reverse(); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "JS_Transform", 3 | "description": "", 4 | "version": "0.1.0", 5 | "private": false, 6 | "author": "Kimonolabs", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/kimonolabs/filter-functions" 10 | }, 11 | "scripts": { 12 | "test": "mocha -R spec" 13 | }, 14 | "engines": { 15 | "node": "0.10.28", 16 | "npm": "2.x" 17 | }, 18 | "dependencies": { 19 | "lodash": "^2.4.1", 20 | "q": "^1.1.2", 21 | "request": "^2.51.0" 22 | }, 23 | "devDependencies": { 24 | "bower": "^1.3.12", 25 | "chai": "^1.9.1", 26 | "mocha": "^2.0.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /default/merge.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var Q = require('q'); 3 | var Util = require('./Util.js'); 4 | 5 | module.exports = function(results, option) { 6 | var collection = option.collection; 7 | var properties = option.properties; 8 | var newProp = option.newProp; 9 | var newProperties = option.newProperties || properties; 10 | 11 | results[collection] = _.map(results[collection], function(e, i) { 12 | var obj = {}; 13 | _.forEach(newProperties, function(key, idx) { 14 | obj[key] = Util.getPropByString(e, properties[idx]); 15 | Util.deletePropByString(e, properties[idx]); 16 | }); 17 | 18 | Util.setPropByString(e, newProp, obj); 19 | return e; 20 | }); 21 | }; 22 | 23 | -------------------------------------------------------------------------------- /default/renameProperty.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var Util = require('./Util.js'); 3 | 4 | module.exports = function(results, option) { 5 | var collection = option.collection; 6 | var properties = option.properties; 7 | var newnames = option.newnames; 8 | var list = results[collection]; 9 | 10 | for (var i = 0, len = list.length; i < len; i++) { 11 | properties.forEach(function(property, idx, arr) { 12 | if(Util.getPropByString(list[i], properties[idx]) === undefined) return; 13 | 14 | var oldval = Util.getPropByString(list[i], property); 15 | Util.deletePropByString(list[i], property); 16 | Util.setPropByString(list[i], newnames[idx], oldval); 17 | }); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /advanced/rsscombine.js: -------------------------------------------------------------------------------- 1 | // for API: https://www.kimonolabs.com/apis/7zrc4qja#advanced 2 | 3 | function transform(data, callback) { 4 | // filter functions are passed the whole API response object 5 | // you may manipulate or add to this data as you want 6 | 7 | // query parameters exist in the global scope, for example: 8 | // http://www.kimonolabs.com/apis//?apikey=&myparam=test 9 | // query.myparam == 'test'; // true 10 | 11 | // pass the transformed data to the callback function 12 | // the callback has a signature (err, callback) 13 | var collection = data.results.collection1; 14 | for (var i = 0; i < collection.length; i++){ 15 | var row = collection[i]; 16 | row.description = row.pubDate + " - " + row.category + " - " + row.author + " - " + row.description; 17 | delete row.author; 18 | delete row.category; 19 | delete row.pubDate; 20 | } 21 | 22 | callback(null, data); 23 | } -------------------------------------------------------------------------------- /advanced/link.js: -------------------------------------------------------------------------------- 1 | //filter function that takes a weird array of url paths and combines them 2 | 3 | //https://www.xing.com/search/in/jobs?q%5Benabled_facets%5D%5B%5D=category_id_code&q%5Benabled_filters%5Bcategory_id_code%5D%5D%5B%5D=11 4 | // Trainee SAP Technology Consultant (m/w) 5 | 6 | 7 | collection = data.results.collection1 8 | 9 | collection.forEach(function(curr, idx, arr) { 10 | var link_array = JSON.parse(curr.link); 11 | arr[i] = link_array.join('/').concat('/'); 12 | }); 13 | -------------------------------------------------------------------------------- /default/split.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var Util = require('./Util.js'); 3 | 4 | module.exports = function(results, option) { 5 | var collection = option.collection; 6 | var property = option.property; 7 | var separator = option.separator; 8 | var names = option.names; 9 | 10 | var prefix = property.split('.').splice(property.length, 1).join('.'); 11 | 12 | results[collection] = _.map(results[collection], function(e, i) { 13 | var vals = Util.getPropByString(e, property).split(separator); 14 | 15 | if(names === undefined) { 16 | names = _.map(vals, function(val, idx, arr) { 17 | return property + '_' + idx; 18 | }); 19 | } 20 | 21 | names = _.map(names, function(name, idx) { 22 | return prefix === '' ? name : prefix + '.' + name; 23 | }); 24 | 25 | _.forEach(names, function(key, idx) { 26 | Util.setPropByString(e, key, vals[idx]); 27 | }); 28 | 29 | Util.deletePropByString(e, property); 30 | return e; 31 | }); 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /default/remove.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var Util = require('./Util.js'); 3 | 4 | var SUPPORTED_OPERATORS = [ 5 | '<', '<=', '>', '>=', '==', '===', '!=', '!==' 6 | ]; 7 | 8 | module.exports = function(results, option) { 9 | var collection = option.collection; 10 | var property = option.property; 11 | var operator = option.operator; 12 | var target = option.target; 13 | 14 | // operator not supported, just skip it 15 | if(!_.contains(SUPPORTED_OPERATORS, operator)) 16 | return this; 17 | 18 | results[collection] = _.filter(results[collection], function(e) { 19 | var val = Util.getPropByString(e, property); 20 | 21 | switch(operator) { 22 | case '<': 23 | return val < target; 24 | case '<=': 25 | return val <= target; 26 | case '>': 27 | return val > target; 28 | case '>=': 29 | return val >= target; 30 | case '==': 31 | return val == target; 32 | case '===': 33 | return val === target; 34 | case '!=': 35 | return val != target; 36 | case '!==': 37 | return val !== target; 38 | } 39 | }); 40 | }; 41 | -------------------------------------------------------------------------------- /default/currencyConvert.js: -------------------------------------------------------------------------------- 1 | var request = require('request'); 2 | var Util = require('./Util.js'); 3 | var Q = require('q'); 4 | var _ = require('lodash'); 5 | 6 | var CURRENCY_API = 'http://finance.yahoo.com/d/quotes.csv?e=.csv&f=sl1d1t1&s=${from}${to}=X'; 7 | 8 | module.exports = function(results, option) { 9 | var collection = option.collection; 10 | var property = option.property; 11 | var from = option.from.toUpperCase(); 12 | var to = option.to.toUpperCase(); 13 | var decimal = option.decimal; 14 | 15 | var deferred = Q.defer(); 16 | var url = _.template(CURRENCY_API, { from: from, to: to }); 17 | request(url, function(err, res, body) { 18 | var ratio = parseFloat(body.split(',')[1]); 19 | _.forEach(results[collection], function(val, key) { 20 | var oldVal = Util.getPropByString(val, property); 21 | var newVal = parseFloat(ratio * oldVal); 22 | 23 | if(decimal !== undefined) { 24 | newVal = parseFloat(newVal.toFixed(decimal)); 25 | } 26 | Util.setPropByString(val, property, newVal); 27 | }); 28 | deferred.resolve(); 29 | }); 30 | return deferred.promise; 31 | }; 32 | -------------------------------------------------------------------------------- /default/Util.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getPropByString: function(obj, propString) { 3 | if (!propString) 4 | return obj; 5 | 6 | var prop, props = propString.split('.'); 7 | 8 | for (var i = 0, iLen = props.length - 1; i < iLen; i++) { 9 | prop = props[i]; 10 | 11 | var candidate = obj[prop]; 12 | if (candidate !== undefined) obj = candidate; 13 | else return; 14 | } 15 | return obj[props[i]]; 16 | }, 17 | 18 | setPropByString: function(obj, propString, val) { 19 | if(!propString) 20 | return obj; 21 | 22 | var prop, props = propString.split('.'); 23 | for (var i = 0, ilen = props.length - 1; i < ilen; i++) { 24 | prop = props[i]; 25 | 26 | var candidate = obj[prop]; 27 | if (candidate !== undefined) obj = candidate; 28 | else return; 29 | } 30 | 31 | obj[props[i]] = val; 32 | }, 33 | 34 | deletePropByString: function(obj, propString) { 35 | if(!propString) 36 | return obj; 37 | 38 | var prop, props = propString.split('.'); 39 | for (var i = 0, len = props.length - 1; i < len; i++) { 40 | prop = props[i]; 41 | 42 | var candidate = obj[prop]; 43 | if (candidate !== undefined) obj = candidate; 44 | else return; 45 | } 46 | 47 | delete obj[props[i]]; 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var Q = require('q'); 3 | var filters = require('./default'); 4 | 5 | function KimFilter(data) { 6 | var self = this; 7 | self.data = data; 8 | self.query = data.query; 9 | self.results = data.results; 10 | 11 | self.currentCollection = ''; 12 | self.tasks = []; 13 | 14 | // decorate methods 15 | _(filters).forEach(function(fn, fn_name) { 16 | self[fn_name] = decorator.call(self, fn); 17 | }); 18 | }; 19 | 20 | 21 | KimFilter.prototype.executeSingleTask = function(task) { 22 | var self = this; 23 | if(!self.query || !self.query.kimbypage) { 24 | return Q(task.fn(self.results, task.option)); 25 | } else { 26 | return Q.all(self.results.map(function(pageResults) { 27 | return Q(task.fn(pageResults, task.option)); 28 | })); 29 | } 30 | }; 31 | 32 | 33 | KimFilter.prototype.executeAllTasks = function() { 34 | var self = this; 35 | return self.tasks.reduce(function(soFar, task) { 36 | return soFar.then(function() { return self.executeSingleTask(task); }); 37 | }, Q(null)); 38 | }; 39 | 40 | 41 | KimFilter.prototype.output = function(fn) { 42 | var self = this; 43 | delete self.data.query; 44 | self.executeAllTasks().then(function() { 45 | fn(null, self.data); 46 | }, function(err){ 47 | fn(err, self.data); 48 | }); 49 | }; 50 | 51 | 52 | KimFilter.prototype.setCurrCollection = function(collection) { 53 | this.currentCollection = collection; 54 | return this; 55 | }; 56 | 57 | 58 | function decorator(fn) { 59 | var self = this; 60 | return function(option) { 61 | // set current collection if not specified in 'option' 62 | option.collection = option.collection || self.currentCollection; 63 | 64 | // enqueue current task 65 | self.tasks.push({ fn: fn, option: option }); 66 | return self; 67 | }; 68 | }; 69 | 70 | module.exports = KimFilter; 71 | -------------------------------------------------------------------------------- /filterfuncExamples.txt: -------------------------------------------------------------------------------- 1 | Filter Function Examples 2 | 3 | - Ex1 4 | 5 | Description: Add a new property 'totalPoints 'to `data`, which is the sum of points of all users in all collections. 6 | 7 | // before 8 | data = { 9 | "name": "sample_input", 10 | "results": { 11 | "collection1": [ 12 | { "ID": "1.", "Karma": "329 points" }, 13 | { "ID": "2.", "Karma": "171 points" }, 14 | { "ID": "3.", "Karma": "145 points" } 15 | ], 16 | "collection2": [ 17 | { "ID": "4.", "Karma": "292 points" }, 18 | { "ID": "5.", "Karma": "142 points" }, 19 | { "ID": "6.", "Karma": "314 points" } 20 | ] 21 | } 22 | }; 23 | 24 | // filter function 25 | var totalPoints = 0; 26 | for(var collection in data.results) { 27 | data.results[collection].forEach(function(row) { 28 | var pts = row.Karma.split(' ')[0]; 29 | totalPoints += parseInt(pts); 30 | }); 31 | }; 32 | data.totalPoints = totalPoints; 33 | 34 | // after 35 | data = { 36 | "name": "sample_input", 37 | "results": { 38 | "collection1": [ 39 | { "ID": "1.", "Karma": "329 points" }, 40 | { "ID": "2.", "Karma": "171 points" }, 41 | { "ID": "3.", "Karma": "145 points" } 42 | ], 43 | "collection2": [ 44 | { "ID": "4.", "Karma": "292 points" }, 45 | { "ID": "5.", "Karma": "142 points" }, 46 | { "ID": "6.", "Karma": "314 points" } 47 | ] 48 | }, 49 | "totalPoints": 1391 50 | }; 51 | 52 | 53 | - Ex2 54 | 55 | Description: Augment each row with the the difference of Karmas between January and Febuary. 56 | 57 | // before 58 | data = { 59 | "name": "sample_input", 60 | "results": { 61 | "collection1": [ 62 | { "ID": "1.", "Karma_Jan": 329, "Karma_Feb": 188 }, 63 | { "ID": "2.", "Karma_Jan": 171, "Karma_Feb": 321 }, 64 | { "ID": "3.", "Karma_Jan": 145, "Karma_Feb": 211 } 65 | ], 66 | "collection2": [ 67 | { "ID": "4.", "Karma_Jan": 292, "Karma_Feb": 461 }, 68 | { "ID": "5.", "Karma_Jan": 142, "Karma_Feb": 131 }, 69 | { "ID": "6.", "Karma_Jan": 314, "Karma_Feb": 410 } 70 | ] 71 | } 72 | }; 73 | 74 | function calcDiff(user) { 75 | user['Karma_Diff'] = user['Karma_Feb'] - user['Karma_Jan']; 76 | return row; 77 | } 78 | 79 | for(var collection in data.results) { 80 | data.results[collection] = data.results[collection].map(calcDiff); 81 | }; 82 | 83 | // after 84 | data = { 85 | "name": "sample_input", 86 | "results": { 87 | "collection1": [ 88 | { "ID": "1.", "Karma_Jan": 329, "Karma_Feb": 188, "Karma_Diff": -141 }, 89 | { "ID": "2.", "Karma_Jan": 171, "Karma_Feb": 321, "Karma_Diff": 150 }, 90 | { "ID": "3.", "Karma_Jan": 145, "Karma_Feb": 211, "Karma_Diff": 66 } 91 | ], 92 | "collection2": [ 93 | { "ID": "4.", "Karma_Jan": 292, "Karma_Feb": 461, "Karma_Diff": 169 }, 94 | { "ID": "5.", "Karma_Jan": 142, "Karma_Feb": 131, "Karma_Diff": -11 }, 95 | { "ID": "6.", "Karma_Jan": 314, "Karma_Feb": 410, "Karma_Diff": 96 } 96 | ] 97 | } 98 | }; 99 | 100 | 101 | - Ex3 102 | 103 | Description: Sort rows in each collection from low to high based on Karma. 104 | 105 | // before 106 | data = { 107 | "name": "sample_input", 108 | "results": { 109 | "collection1": [ 110 | { "ID": "1.", "Karma": 329 }, 111 | { "ID": "2.", "Karma": 171 }, 112 | { "ID": "3.", "Karma": 145 } 113 | ], 114 | "collection2": [ 115 | { "ID": "4.", "Karma": 292 }, 116 | { "ID": "5.", "Karma": 142 }, 117 | { "ID": "6.", "Karma": 314 } 118 | ] 119 | } 120 | }; 121 | 122 | for(var collection in data.results) { 123 | data.results[collection].sort(function(a, b) { 124 | return a.Karma - b.Karma; 125 | }); 126 | } 127 | 128 | // after 129 | data = { 130 | "name": "sample_input", 131 | "results": { 132 | "collection1": [ 133 | { "ID": "3.", "Karma": 145 } 134 | { "ID": "2.", "Karma": 171 }, 135 | { "ID": "1.", "Karma": 329 }, 136 | ], 137 | "collection2": [ 138 | { "ID": "5.", "Karma": 142 }, 139 | { "ID": "4.", "Karma": 292 }, 140 | { "ID": "6.", "Karma": 314 } 141 | ] 142 | } 143 | }; 144 | 145 | 146 | - Ex4 147 | 148 | Description: filter out users(i.e rows) with less than 200 Karma. 149 | 150 | // before 151 | data = { 152 | "name": "sample_input", 153 | "results": { 154 | "collection1": [ 155 | { "ID": "1.", "Karma": 329 }, 156 | { "ID": "2.", "Karma": 171 }, 157 | { "ID": "3.", "Karma": 145 } 158 | ], 159 | "collection2": [ 160 | { "ID": "4.", "Karma": 292 }, 161 | { "ID": "5.", "Karma": 142 }, 162 | { "ID": "6.", "Karma": 314 } 163 | ] 164 | } 165 | }; 166 | 167 | function isTopUser(user) { 168 | return user.Karma >= 200; 169 | } 170 | 171 | // filter function 172 | for(var collection in data.results) { 173 | data.results[collection].filter(isTopUser); 174 | }; 175 | 176 | // after 177 | data = { 178 | "name": "sample_input", 179 | "results": { 180 | "collection1": [ 181 | { "ID": "1.", "Karma": 329 } 182 | ], 183 | "collection2": [ 184 | { "ID": "4.", "Karma": 292 }, 185 | { "ID": "6.", "Karma": 314 } 186 | ] 187 | } 188 | }; 189 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A collection of filter functions. 2 | 3 | #Default transforms 4 | - split 5 | - sort 6 | - remove 7 | - merge 8 | - removeProp 9 | - renameProperty 10 | - renameCollection 11 | - replace 12 | - toInt 13 | - toFloat 14 | - custom 15 | - deep_selection 16 | 17 | -------------------------------------------------------------- 18 | #Documentation 19 | 20 | ####**split**(*option*) 21 | 22 | Split one string property into multiple properties according to the separator. 23 | 24 | #####Arguments 25 | - option (Object): The configuration of this transform. It should contain following properties: 26 | 27 | - *collection* (String): The collection being modified. Optional if current collection has been set previously via *setCurrentCollection()* 28 | - *property* (String): The property whose value is being splited. The value being splited must be a string. 29 | - *separator* (String|RegExp): The character, or regular expression, to use for splitting the string. 30 | - *names*: (Array[String]): Optional. An array of strings that specifies the names of new properties generated by the split. If not provided, they will be generated automatically by appending 0, 1, 2, ... to the end of *property* specified above. 31 | 32 | #####Returns 33 | - The *this* binding of the KimFilter object being applied. 34 | 35 | #####Example 36 | 37 | ```javascript 38 | var data = { 39 | "name": "sample_input", 40 | "results": { 41 | "collection1": [ 42 | { "ID": "1.", "Karma": "329 points" }, 43 | { "ID": "2.", "Karma": "171 points" }, 44 | { "ID": "3.", "Karma": "145 points" } 45 | ] 46 | } 47 | }; 48 | 49 | new KimFilter(data) 50 | .setCurrentCollection("collection1") 51 | .split({ 52 | property: "Karma", 53 | separator: " ", 54 | names: ["Num", "Unit"] 55 | }) 56 | .output(function(data) { 57 | console.log(data); 58 | }); 59 | 60 | // will print 61 | // { 62 | // "name": "sample_input", 63 | // "results": { 64 | // "collection1": [ 65 | // { "ID": "1.", "Num": "329", "Unit": "points" }, 66 | // { "ID": "2.", "Num": "171", "Unit": "points" }, 67 | // { "ID": "3.", "Num": "145", "Unit": "points" } 68 | // ] 69 | // } 70 | // }; 71 | ``` 72 | 73 | -------------------------------------------------------------- 74 | 75 | ####**sort**(*option*) 76 | 77 | Sort a collection according to the specified property. 78 | 79 | #####Arguments 80 | - option (Object): The configuration of this transform. It should contain following properties: 81 | 82 | - *collection* (String): The collection being modified. Optional if current collection has been set previously via *setCurrentCollection()* 83 | - *property* (String): The property being compared during sort. 84 | - *lowToHigh* (boolean): Set to 1 to sort in ascending order, 0 otherwise. 85 | 86 | #####Returns 87 | - The *this* binding of the KimFilter object being applied. 88 | 89 | #####Example 90 | 91 | ```javascript 92 | var data = { 93 | "name": "sample_input", 94 | "results": { 95 | "collection1": [ 96 | { "ID": "1.", "Karma": "329 points" }, 97 | { "ID": "2.", "Karma": "171 points" }, 98 | { "ID": "3.", "Karma": "145 points" } 99 | ] 100 | } 101 | }; 102 | 103 | new KimFilter(data) 104 | .setCurrentCollection("collection1") 105 | .split({ 106 | property: "Karma", 107 | lowToHigh: 1 108 | }) 109 | .output(function(data) { 110 | console.log(data); 111 | }); 112 | 113 | // will print 114 | // { 115 | // "name": "sample_input", 116 | // "results": { 117 | // "collection1": [ 118 | // { "ID": "3.", "Num": "145", "Unit": "points" } 119 | // { "ID": "2.", "Num": "171", "Unit": "points" }, 120 | // { "ID": "1.", "Num": "329", "Unit": "points" }, 121 | // ] 122 | // } 123 | // }; 124 | ``` 125 | 126 | 127 | -------------------------------------------------------------- 128 | 129 | ####**remove**(*option*) 130 | 131 | Remove items in a collection that does not satisfy given conditions 132 | 133 | #####Arguments 134 | - option (Object): The configuration of this transform. It should contain following properties: 135 | 136 | - *collection* (String): The collection being modified. Optional if current collection has been set previously via *setCurrentCollection()* 137 | - *property* (String): The property being tested. 138 | - *opreator* (String): The operator being used when comparing property against given target. The operator must be one of <, <=, >, >=, ==, ===, !=, !==. 139 | - *target* (String|Number|Undefined): The target value being compared against when testing. 140 | 141 | #####Returns 142 | - The *this* binding of the KimFilter object being applied. 143 | 144 | #####Example 145 | 146 | ```javascript 147 | var data = { 148 | "name": "sample_input", 149 | "results": { 150 | "collection1": [ 151 | { "ID": "1", "Karma": "329 points" }, 152 | { "ID": "2", "Karma": "171 points" }, 153 | { "ID": "3", "Karma": "145 points" } 154 | { "ID": "4", "Karma": "49 points" }, 155 | { "ID": "5", "Karma": "129 points" } 156 | ] 157 | } 158 | }; 159 | 160 | new KimFilter(data) 161 | .setCurrentCollection("collection1") 162 | .remove({ 163 | property: 'ID', 164 | operator: '<=', 165 | target: 2 166 | }) 167 | .output(function(data) { 168 | console.log(data); 169 | }); 170 | 171 | // will print 172 | // { 173 | // "name": "sample_input", 174 | // "results": { 175 | // "collection1": [ 176 | // { "ID": "1.", "Num": "329", "Unit": "points" }, 177 | // { "ID": "2.", "Num": "171", "Unit": "points" }, 178 | // ] 179 | // } 180 | // }; 181 | ``` 182 | 183 | 184 | -------------------------------------------------------------- 185 | 186 | ####**merge**(*option*) 187 | 188 | Merge mutlitple properties into one property. 189 | 190 | #####Arguments 191 | - option (Object): The configuration of this transform. It should contain following properties: 192 | 193 | - *collection* (String): The collection being modified. Optional if current collection has been set previously via *setCurrentCollection()* 194 | - *properties* (Array[String]): An array of properties being merged. 195 | - *newProp* (String): The name of merged property. 196 | - *newProperties* (Array[String]): An array of new names of properties being merged. Old names will be used if new names are not provided. 197 | 198 | #####Returns 199 | - The *this* binding of the KimFilter object being applied. 200 | 201 | #####Example 202 | 203 | ```javascript 204 | var data = { 205 | "name": "sample_input", 206 | "results": { 207 | "collection1": [ 208 | { "ID": "1", "Karma": "329 points", "href": "https://abc.com" }, 209 | { "ID": "2", "Karma": "171 points", "href": "https://def.com" }, 210 | { "ID": "3", "Karma": "145 points", "href": "https://hij.com" }, 211 | { "ID": "4", "Karma": "49 points", "href": "https://klm.com" }, 212 | { "ID": "5", "Karma": "129 points", "href": "https://nop.com" } 213 | ] 214 | } 215 | }; 216 | 217 | new KimFilter(data) 218 | .setCurrentCollection("collection1") 219 | .merge({ 220 | properties: ['Karma', 'href'], 221 | newProp: 'Data', 222 | newProperies: ['data_karma', 'data_href'] 223 | }) 224 | .output(function(data) { 225 | console.log(data); 226 | }); 227 | 228 | // will print 229 | // { 230 | // "name": "sample_input", 231 | // "results": { 232 | // "collection1": [ 233 | // { "ID": "1", Data: { "data_karma": "329 points", "data_href": "https://abc.com" }}, 234 | // { "ID": "2", Data: { "data_karma": "171 points", "data_href": "https://def.com" }}, 235 | // { "ID": "3", Data: { "data_karma": "145 points", "data_href": "https://hij.com" }}, 236 | // { "ID": "4", Data: { "data_karma": "49 points", "data_href": "https://klm.com" }}, 237 | // { "ID": "5", Data: { "data_karma": "129 points", "data_href": "https://nop.com" }} 238 | // ] 239 | // } 240 | // } 241 | ``` 242 | 243 | 244 | -------------------------------------------------------------- 245 | 246 | ####**removeProp**(*option*) 247 | 248 | Remove one or more properteis from collection. 249 | 250 | #####Arguments 251 | - option (Object): The configuration of this transform. It should contain following properties: 252 | 253 | - *collection* (String): The collection being modified. Optional if current collection has been set previously via *setCurrentCollection()* 254 | - *properties* (Array[String]): An array of properties being removed. 255 | 256 | #####Returns 257 | - The *this* binding of the KimFilter object being applied. 258 | 259 | #####Example 260 | 261 | ```javascript 262 | var data = { 263 | "name": "sample_input", 264 | "results": { 265 | "collection1": [ 266 | { "ID": "1", "Karma": "329 points", "href": "https://abc.com" }, 267 | { "ID": "2", "Karma": "171 points", "href": "https://def.com" }, 268 | { "ID": "3", "Karma": "145 points", "href": "https://hij.com" }, 269 | { "ID": "4", "Karma": "49 points", "href": "https://klm.com" }, 270 | { "ID": "5", "Karma": "129 points", "href": "https://nop.com" } 271 | ] 272 | } 273 | }; 274 | 275 | new KimFilter(data) 276 | .setCurrentCollection("collection1") 277 | .remove({ 278 | properties: ['Karma', 'href'] 279 | }) 280 | .output(function(data) { 281 | console.log(data); 282 | }); 283 | 284 | // will print 285 | // { 286 | // "name": "sample_input", 287 | // "results": { 288 | // "collection1": [ 289 | // { "ID": "1" }, 290 | // { "ID": "2" }, 291 | // { "ID": "3" }, 292 | // { "ID": "4" }, 293 | // { "ID": "5" } 294 | // ] 295 | // } 296 | // }; 297 | ``` 298 | 299 | 300 | -------------------------------------------------------------- 301 | 302 | ####**renameProperty**(*option*) 303 | 304 | Rename one properties. 305 | 306 | #####Arguments 307 | - option (Object): The configuration of this transform. It should contain following properties: 308 | 309 | - *collection* (String): The collection being modified. Optional if current collection has been set previously via *setCurrentCollection()*. 310 | - *properties* (Array[String]): An array of properties being renamed. 311 | - *newnames* (Array[String]): An array of new names to be used. 312 | 313 | 314 | #####Returns 315 | - The *this* binding of the KimFilter object being applied. 316 | 317 | #####Example 318 | 319 | ```javascript 320 | var data = { 321 | "name": "sample_input", 322 | "results": { 323 | "collection1": [ 324 | { "ID": "1", Data: { "Karma": "329 points", "href": "https://abc.com" }}, 325 | { "ID": "2", Data: { "Karma": "171 points", "href": "https://def.com" }}, 326 | { "ID": "3", Data: { "Karma": "145 points", "href": "https://hij.com" }}, 327 | { "ID": "4", Data: { "Karma": "49 points", "href": "https://klm.com" }}, 328 | { "ID": "5", Data: { "Karma": "129 points", "href": "https://nop.com" }} 329 | ] 330 | } 331 | }; 332 | 333 | new KimFilter(data) 334 | .setCurrentCollection("collection1") 335 | .renameProperty({ 336 | property: ['ID', 'Data.href'], 337 | newname: ['id', 'Data.link'] 338 | }) 339 | .output(function(data) { 340 | console.log(data); 341 | }); 342 | 343 | // // will print 344 | // var data = { 345 | // "name": "sample_input", 346 | // "results": { 347 | // "collection1": [ 348 | // { "id": "1", Data: { "Karma": "329 points", "link": "https://abc.com" }}, 349 | // { "id": "2", Data: { "Karma": "171 points", "link": "https://def.com" }}, 350 | // { "id": "3", Data: { "Karma": "145 points", "link": "https://hij.com" }}, 351 | // { "id": "4", Data: { "Karma": "49 points", "link": "https://klm.com" }}, 352 | // { "id": "5", Data: { "Karma": "129 points", "link": "https://nop.com" }} 353 | // ] 354 | // } 355 | // }; 356 | ``` 357 | 358 | 359 | -------------------------------------------------------------- 360 | 361 | ####**renameCollection**(*option*) 362 | 363 | Rename one collection. 364 | 365 | #####Arguments 366 | - option (Object): The configuration of this transform. It should contain following properties: 367 | 368 | - *collection* (String): The collection being named. Optional if current collection has been set previously via *setCurrentCollection()*. 369 | - *newname* (String): The new name to be used. 370 | 371 | Note: if the collection being renamed is the current collection(i.e. via a previous call to *setCurrentCollection()*), then current collection will be 372 | renamed automatically. 373 | 374 | 375 | #####Returns 376 | - The *this* binding of the KimFilter object being applied. 377 | 378 | #####Example 379 | 380 | ```javascript 381 | var data = { 382 | "name": "sample_input", 383 | "results": { 384 | "collection1": [ 385 | { "ID": "1", "Karma": "329 points", "href": "https://abc.com" }, 386 | { "ID": "2", "Karma": "171 points", "href": "https://def.com" }, 387 | { "ID": "3", "Karma": "145 points", "href": "https://hij.com" }, 388 | { "ID": "4", "Karma": "49 points", "href": "https://klm.com" }, 389 | { "ID": "5", "Karma": "129 points", "href": "https://nop.com" } 390 | ] 391 | } 392 | }; 393 | 394 | new KimFilter(data) 395 | .setCurrentCollection("collection1") 396 | .renameCollection({ 397 | newname: 'HackerNews' 398 | }) 399 | .output(function(data) { 400 | console.log(data); 401 | }); 402 | 403 | // // will print 404 | // var data = { 405 | // "name": "sample_input", 406 | // "results": { 407 | // "HackerNews": [ 408 | // { "ID": "1", "Karma": "329 points", "href": "https://abc.com" }, 409 | // { "ID": "2", "Karma": "171 points", "href": "https://def.com" }, 410 | // { "ID": "3", "Karma": "145 points", "href": "https://hij.com" }, 411 | // { "ID": "4", "Karma": "49 points", "href": "https://klm.com" }, 412 | // { "ID": "5", "Karma": "129 points", "href": "https://nop.com" } 413 | // ] 414 | // } 415 | // }; 416 | ``` 417 | 418 | 419 | -------------------------------------------------------------- 420 | 421 | ####**replace**(*option*) 422 | 423 | The replace method searches a string for a specified value, or a regular expression, and replace the matched pattern with the given string. 424 | 425 | #####Arguments 426 | - option (Object): The configuration of this transform. It should contain following properties: 427 | 428 | - *collection* (String): The collection being named. Optional if current collection has been set previously via *setCurrentCollection()*. 429 | - *property* (String): The property whose value is being searched and modified. 430 | - *from* (String|RegExp): The value, or regular expression, that will be replaced by the new value. 431 | - *to* (String): The value to replace the *from* with. 432 | 433 | #####Returns 434 | - The *this* binding of the KimFilter object being applied. 435 | 436 | #####Example 437 | 438 | ```javascript 439 | var data = { 440 | "name": "sample_input", 441 | "results": { 442 | "collection1": [ 443 | { "ID": "1", "Karma": "329 points", "href": "https://abc.com" }, 444 | { "ID": "2", "Karma": "171 points", "href": "https://def.com" }, 445 | { "ID": "3", "Karma": "145 points", "href": "https://hij.com" }, 446 | { "ID": "4", "Karma": "49 points", "href": "https://klm.com" }, 447 | { "ID": "5", "Karma": "129 points", "href": "https://nop.com" } 448 | ] 449 | } 450 | }; 451 | 452 | new KimFilter(data) 453 | .setCurrentCollection("collection1") 454 | .replace({ 455 | property: 'href', 456 | from: 'https', 457 | to: 'http' 458 | }) 459 | .output(function(data) { 460 | console.log(data); 461 | }); 462 | 463 | // // will print 464 | // var data = { 465 | // "name": "sample_input", 466 | // "results": { 467 | // "HackerNews": [ 468 | // { "ID": "1", "Karma": "329 points", "href": "http://abc.com" }, 469 | // { "ID": "2", "Karma": "171 points", "href": "http://def.com" }, 470 | // { "ID": "3", "Karma": "145 points", "href": "http://hij.com" }, 471 | // { "ID": "4", "Karma": "49 points", "href": "http://klm.com" }, 472 | // { "ID": "5", "Karma": "129 points", "href": "http://nop.com" } 473 | // ] 474 | // } 475 | // }; 476 | ``` 477 | 478 | 479 | -------------------------------------------------------------- 480 | 481 | ####**toInt**(*option*) 482 | 483 | Convert string to integer. 484 | 485 | #####Arguments 486 | - option (Object): The configuration of this transform. It should contain following properties: 487 | 488 | - *collection* (String): The collection being named. Optional if current collection has been set previously via *setCurrentCollection()*. 489 | - *property* (String): The property whose value is being converted. 490 | 491 | #####Returns 492 | - The *this* binding of the KimFilter object being applied. 493 | 494 | #####Example 495 | 496 | ```javascript 497 | var data = { 498 | "name": "sample_input", 499 | "results": { 500 | "collection1": [ 501 | { "ID": "1", "Karma": "329 points", "href": "https://abc.com" }, 502 | { "ID": "2", "Karma": "171 points", "href": "https://def.com" }, 503 | { "ID": "3", "Karma": "145 points", "href": "https://hij.com" }, 504 | { "ID": "4", "Karma": "49 points", "href": "https://klm.com" }, 505 | { "ID": "5", "Karma": "129 points", "href": "https://nop.com" } 506 | ] 507 | } 508 | }; 509 | 510 | new KimFilter(data) 511 | .setCurrentCollection("collection1") 512 | .toInt({ 513 | property: 'ID' 514 | }) 515 | .output(function(data) { 516 | console.log(data); 517 | }); 518 | 519 | // // will print 520 | // var data = { 521 | // "name": "sample_input", 522 | // "results": { 523 | // "HackerNews": [ 524 | // { "ID": 1, "Karma": "329 points", "href": "https://abc.com" }, 525 | // { "ID": 2, "Karma": "171 points", "href": "https://def.com" }, 526 | // { "ID": 3, "Karma": "145 points", "href": "https://hij.com" }, 527 | // { "ID": 4, "Karma": "49 points", "href": "https://klm.com" }, 528 | // { "ID": 5, "Karma": "129 points", "href": "https://nop.com" } 529 | // ] 530 | // } 531 | // }; 532 | ``` 533 | 534 | 535 | -------------------------------------------------------------- 536 | 537 | ####**toFloat**(*option*) 538 | 539 | Convert string to floating point number. 540 | 541 | #####Arguments 542 | - option (Object): The configuration of this transform. It should contain following properties: 543 | 544 | - *collection* (String): The collection being named. Optional if current collection has been set previously via *setCurrentCollection()*. 545 | - *property* (String): The property whose value is being converted. 546 | - *decimal* (String): The number of digits to appear after the decimal point; this may be a value between 0 and 20, inclusive, and implementations may optionally support a larger range of values. If this argument is omitted, it is treated as 0. 547 | 548 | #####Returns 549 | - The *this* binding of the KimFilter object being applied. 550 | 551 | #####Example 552 | 553 | ```javascript 554 | var data = { 555 | "name": "sample_input", 556 | "results": { 557 | "collection1": [ 558 | { "ID": "1", "Karma": "329.21331", "href": "https://abc.com" }, 559 | { "ID": "2", "Karma": "171.321", "href": "https://def.com" }, 560 | { "ID": "3", "Karma": "145.943", "href": "https://hij.com" }, 561 | { "ID": "4", "Karma": "49.123213", "href": "https://klm.com" }, 562 | { "ID": "5", "Karma": "129.9", "href": "https://nop.com" } 563 | ] 564 | } 565 | }; 566 | 567 | new KimFilter(data) 568 | .setCurrentCollection("collection1") 569 | .toFloat({ 570 | property: 'Karma', 571 | decimal: 3 572 | }) 573 | .output(function(data) { 574 | console.log(data); 575 | }); 576 | 577 | // // will print 578 | // var data = { 579 | // "name": "sample_input", 580 | // "results": { 581 | // "HackerNews": [ 582 | // { "ID": "1", "Karma": 329.213, "href": "https://abc.com" }, 583 | // { "ID": "2", "Karma": 171.321, "href": "https://def.com" }, 584 | // { "ID": "3", "Karma": 145.943, "href": "https://hij.com" }, 585 | // { "ID": "4", "Karma": 49.123, "href": "https://klm.com" }, 586 | // { "ID": "5", "Karma": 129.900, "href": "https://nop.com" } 587 | // ] 588 | // } 589 | // }; 590 | ``` 591 | 592 | 593 | -------------------------------------------------------------- 594 | 595 | ####**custom**(*fn*) 596 | 597 | Apply user-defined transform functions. 598 | 599 | #####Arguments 600 | - fn (Function): The user-defined transform function being applied. The *results* object will be passed to this function. 601 | 602 | #####Returns 603 | - The *this* binding of the KimFilter object being applied. 604 | 605 | #####Example 606 | 607 | ```javascript 608 | var data = { 609 | "name": "sample_input", 610 | "results": { 611 | "collection1": [ 612 | { "ID": "1", "Karma": "329.21331", "href": "https://abc.com" }, 613 | { "ID": "2", "Karma": "171.321", "href": "https://def.com" }, 614 | { "ID": "3", "Karma": "145.943", "href": "https://hij.com" }, 615 | { "ID": "4", "Karma": "49.123213", "href": "https://klm.com" }, 616 | { "ID": "5", "Karma": "129.9", "href": "https://nop.com" } 617 | ] 618 | } 619 | }; 620 | 621 | new KimFilter(data) 622 | .custom(function(results) { 623 | var collection = 'HackerNews'; 624 | results[collection].forEach(function(entry, idx, arr) { 625 | entry['timestamp'] = (new Date()).toUTCString(); 626 | }); 627 | }) 628 | .output(function(data) { 629 | console.log(data); 630 | }); 631 | 632 | // // will print 633 | // var data = { 634 | // "name": "sample_input", 635 | // "results": { 636 | // "HackerNews": [ 637 | // { "ID": "1", "Karma": 329.213, "href": "https://abc.com", "timestamp": "Mon, 12, Jan 2015 3:14:15 GMT" }, 638 | // { "ID": "2", "Karma": 171.321, "href": "https://def.com", "timestamp": "Mon, 12, Jan 2015 3:14:15 GMT" }, 639 | // { "ID": "3", "Karma": 145.943, "href": "https://hij.com", "timestamp": "Mon, 12, Jan 2015 3:14:15 GMT" }, 640 | // { "ID": "4", "Karma": 49.123, "href": "https://klm.com", "timestamp": "Mon, 12, Jan 2015 3:14:15 GMT" }, 641 | // { "ID": "5", "Karma": 129.900, "href": "https://nop.com", "timestamp": "Mon, 12, Jan 2015 3:14:15 GMT" } 642 | // ] 643 | // } 644 | // }; 645 | ``` 646 | -------------------------------------------------------------- 647 | 648 | ####Access/modify nested properties in JSON 649 | To access/modify properties that are nested in an object, dot notation can be used. For example, to sort collection 650 | based on 'karma' in ascending order, you can do this: 651 | 652 | ```javascript 653 | var data = { 654 | "name": "sample_input", 655 | "results": { 656 | "collection1": [ 657 | { "ID": "1.", Data: { "Karma": 329, "href": "https://test.com" }}, 658 | { "ID": "2.", Data: { "Karma": 171, "href": "https://test.com" }}, 659 | { "ID": "3.", Data: { "Karma": 145, "href": "https://test.com" }} 660 | ] 661 | } 662 | }; 663 | 664 | new KimFilter(data) 665 | .setCurrentCollection("collection1") 666 | .split({ 667 | property: "Data.Karma", 668 | lowToHigh: 1 669 | }) 670 | .output(function(data) { 671 | console.log(data); 672 | }); 673 | 674 | // will print 675 | // var data = { 676 | // "name": "sample_input", 677 | // "results": { 678 | // "collection1": [ 679 | // { "ID": "3.", Data: { "Karma": 145, "href": "https://test.com" }}, 680 | // { "ID": "2.", Data: { "Karma": 171, "href": "https://test.com" }}, 681 | // { "ID": "1.", Data: { "Karma": 329, "href": "https://test.com" }}, 682 | // ] 683 | // } 684 | // }; 685 | ``` 686 | 687 | -------------------------------------------------------------------------------- /test/test_default_transforms.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var _ = require('lodash'); 3 | var KimFilter = require('../index.js'); 4 | var Util = require('../default/Util.js'); 5 | 6 | describe('Kimono JS Transforms', function() { 7 | describe('Basic', function() { 8 | var testData; 9 | beforeEach(function() { 10 | testData = { name: 'testData', results: { 'c1': [ { key: 1, val: "data1"}, { key: 2, val: "data2"}, { key: 3, val: "data3" }]}}; 11 | }); 12 | 13 | it('should have 3 entires', function() { 14 | assert.equal(testData.results['c1'].length, 3); 15 | }); 16 | 17 | it('`output()` should not modify data', function(done) { 18 | (new KimFilter(testData)) 19 | .output(function(err, copy){ 20 | assert.equal(_.isEqual(testData, copy), true); 21 | done(); 22 | }); 23 | }); 24 | 25 | it('`setCurrCollection()` should not modify data', function(done) { 26 | new KimFilter(testData) 27 | .setCurrCollection('NewCollection') 28 | .output(function(err, copy) { 29 | assert.equal(_.isEqual(testData, copy), true); 30 | done(); 31 | }); 32 | }); 33 | }); 34 | 35 | describe('Default Transforms', function() { 36 | var testData; 37 | beforeEach(function() { 38 | testData = { name: 'testData', results: { 'c1': [ { key: 1, val: "data1"}, { key: 2, val: "data2"}, { key: 3, val: "data3" }]}}; 39 | }); 40 | 41 | //================================================================== 42 | // Sort 43 | //================================================================== 44 | describe('Sort', function() { 45 | it('should sort data in descending order', function(done) { 46 | (new KimFilter(testData)) 47 | .setCurrCollection('c1') 48 | .sort({ 49 | property: 'key', 50 | lowToHigh: 0 51 | }) 52 | .output(function(err, result) { 53 | var sorted = { name: 'testData', results: { 'c1': [ { key: 3, val: "data3"}, { key: 2, val: "data2"}, { key: 1, val: "data1" }]}}; 54 | assert.equal(_.isEqual(result, sorted), true); 55 | done(); 56 | }); 57 | }); 58 | 59 | it('should sorty data in ascending order', function(done) { 60 | (new KimFilter(testData)) 61 | .setCurrCollection('c1') 62 | .sort({ 63 | property: 'key', 64 | }) 65 | .output(function(err, result) { 66 | var sorted = { name: 'testData', results: { 'c1': [ { key: 1, val: "data1"}, { key: 2, val: "data2"}, { key: 3, val: "data3" }]}}; 67 | 68 | assert.equal(_.isEqual(result, sorted), true); 69 | done(); 70 | }); 71 | }); 72 | }); 73 | 74 | 75 | //================================================================== 76 | // Replace 77 | //================================================================== 78 | describe('Replace', function() { 79 | it('should not modify input data(empty pattern)', function(done) { 80 | (new KimFilter(testData)) 81 | .setCurrCollection('c1') 82 | .replace({ 83 | property: 'val', 84 | from: '', 85 | to: '' 86 | }) 87 | .output(function(err, result) { 88 | var replaced = { name: 'testData', results: { 'c1': [ { key: 1, val: "data1"}, { key: 2, val: "data2"}, { key: 3, val: "data3" }]}}; 89 | assert.equal(_.isEqual(replaced, result), true); 90 | done(); 91 | }); 92 | }); 93 | 94 | 95 | it('should not modify input data(non-matched pattern)', function(done) { 96 | (new KimFilter(testData)) 97 | .setCurrCollection('c1') 98 | .replace({ 99 | property: 'val', 100 | from: '123', 101 | to: '()' 102 | }) 103 | .output(function(err, result) { 104 | var replaced = { name: 'testData', results: { 'c1': [ { key: 1, val: "data1"}, { key: 2, val: "data2"}, { key: 3, val: "data3" }]}}; 105 | assert.equal(_.isEqual(replaced, result), true); 106 | done(); 107 | }); 108 | }); 109 | 110 | 111 | it('should replace `data` with `value`', function(done) { 112 | (new KimFilter(testData)) 113 | .setCurrCollection('c1') 114 | .replace({ 115 | property: 'val', 116 | from: 'data', 117 | to: 'value' 118 | }) 119 | .output(function(err, result) { 120 | var replaced = { name: 'testData', results: { 'c1': [ { key: 1, val: "value1"}, { key: 2, val: "value2"}, { key: 3, val: "value3" }]}}; 121 | assert.equal(_.isEqual(replaced, result), true); 122 | done(); 123 | }); 124 | }); 125 | 126 | it('should replace the trailing number of `val` with a `.`', function(done) { 127 | (new KimFilter(testData)) 128 | .setCurrCollection('c1') 129 | .replace({ 130 | property: 'val', 131 | from: /[123]$/, 132 | to: '.' 133 | }) 134 | .output(function(err, result) { 135 | var replaced = { name: 'testData', results: { 'c1': [ { key: 1, val: "data."}, { key: 2, val: "data."}, { key: 3, val: "data." }]}}; 136 | assert.equal(_.isEqual(replaced, result), true); 137 | done(); 138 | }); 139 | }); 140 | }); 141 | 142 | 143 | //================================================================== 144 | // Split 145 | //================================================================== 146 | describe('Split', function() { 147 | var testData; 148 | beforeEach(function() { 149 | testData = { name: 'testData', results: { 'c1': [ { key: 1, val: "data 1"}, { key: 2, val: "data 2"}, { key: 3, val: "data 3" }]}}; 150 | }); 151 | 152 | it('should split `val` into `first` and `second`', function() { 153 | (new KimFilter(testData)) 154 | .setCurrCollection('c1') 155 | .split({ 156 | property: 'val', 157 | separator: ' ', 158 | names: ['first', 'second'] 159 | }) 160 | .output(function(err, result) { 161 | var splited = { name: 'testData', results: { 'c1': [ { key: 1, first: 'data', second: '1'}, { key: 2, first: 'data', second: '2'}, { key: 3, first: 'data', second: '3'}]}}; 162 | assert.equal(_.isEqual(splited, result), true); 163 | done(); 164 | }); 165 | }); 166 | 167 | it('should split `val` into `val_1` and `val_2`(`names` not provided)', function() { 168 | (new KimFilter(testData)) 169 | .setCurrCollection('c1') 170 | .split({ 171 | property: 'val', 172 | separator: ' ', 173 | }) 174 | .output(function(err, result) { 175 | var splited = { name: 'testData', results: { 'c1': [ { key: 1, val_1: 'data', val_2: '1'}, { key: 2, val_1: 'data', val_2: '2'}, { key: 3, val_1: 'data', val_2: '3'}]}}; 176 | assert.equal(_.isEqual(splited, result), true); 177 | done(); 178 | }); 179 | }); 180 | 181 | it('should only keep the first k properties if the length of `names` is k', function() { 182 | (new KimFilter(testData)) 183 | .setCurrCollection('c1') 184 | .split({ 185 | property: 'val', 186 | separator: ' ', 187 | names: ['first'] 188 | }) 189 | .output(function(err, result) { 190 | var splited = { name: 'testData', results: { 'c1': [ { key: 1, first: 'data' }, { key: 2, first: 'data' }, { key: 3, first: 'data' }]}}; 191 | assert.deepEqual(result, splited); 192 | done(); 193 | }); 194 | }); 195 | 196 | it('should not split the value of property (separator not found)', function() { 197 | (new KimFilter(testData)) 198 | .setCurrCollection('c1') 199 | .split({ 200 | property: 'val', 201 | separator: '*', 202 | }) 203 | .output(function(err, result) { 204 | var splited = { name: 'testData', results: { 'c1': [ { key: 1, val: "data 1"}, { key: 2, val: "data 2"}, { key: 3, val: "data 3" }]}}; 205 | assert.deepEqual(result, splited); 206 | done(); 207 | }); 208 | }); 209 | 210 | it('should not split the value of property but rename the property (only 1 element generated after split)', function() { 211 | (new KimFilter(testData)) 212 | .setCurrCollection('c1') 213 | .split({ 214 | property: 'val', 215 | separator: '*', 216 | names: ['newval'] 217 | }) 218 | .output(function(err, result) { 219 | var splited = { name: 'testData', results: { 'c1': [ { key: 1, newval: "data 1"}, { key: 2, newval: "data 2"}, { key: 3, newval: "data 3" }]}}; 220 | assert.equal(_.isEqual(splited, result), true); 221 | done(); 222 | }); 223 | }); 224 | }); 225 | 226 | //================================================================== 227 | // Merge 228 | //================================================================== 229 | describe('Merge', function() { 230 | var testData; 231 | beforeEach(function() { 232 | testData = { name: 'testData', results: { 'c1': [ { key: 1, val: "data 1"}, { key: 2, val: "data 2"}, { key: 3, val: "data 3" }]}}; 233 | }); 234 | 235 | it('should merge `key` and `val` into `pair` while keep the attribute names', function(done) { 236 | (new KimFilter(testData)) 237 | .setCurrCollection('c1') 238 | .merge({ 239 | properties: ['key', 'val'], 240 | newProp: 'key_val', 241 | }) 242 | .output(function(err, result) { 243 | var merged = { name: 'testData', results: { 'c1': [ { key_val: { key: 1, val: "data 1"} }, { key_val: { key: 2, val: "data 2"} }, { key_val: { key: 3, val: "data 3"} } ]}}; 244 | assert.deepEqual(merged, result); 245 | done(); 246 | }); 247 | }); 248 | 249 | it('should merge `key` and `val` into `pair` while renaming `key` to `subkey` and `val` to `subval`', function(done) { 250 | (new KimFilter(testData)) 251 | .setCurrCollection('c1') 252 | .merge({ 253 | properties: ['key', 'val'], 254 | newProp: 'key_val', 255 | newProperties: ['subkey', 'subval'] 256 | }) 257 | .output(function(err, result) { 258 | var merged = { name: 'testData', results: { 'c1': [ { key_val: { subkey: 1, subval: "data 1"} }, { key_val: { subkey: 2, subval: "data 2"} }, { key_val: { subkey: 3, subval: "data 3"} } ]}}; 259 | assert.deepEqual(merged, result); 260 | done(); 261 | }); 262 | }); 263 | }); 264 | 265 | //================================================================== 266 | // toInt 267 | //================================================================== 268 | describe('toInt', function() { 269 | var testData; 270 | beforeEach(function() { 271 | testData = { name: 'testData', results: { 'c1': [ { key: 1, val: "data 1"}, { key: 2, val: "data 2"}, { key: 3, val: "data 3" }]}}; 272 | }); 273 | 274 | it('should convert char to integer', function(done) { 275 | (new KimFilter(testData) 276 | .setCurrCollection('c1') 277 | .split({ 278 | property: 'val', 279 | separator: ' ', 280 | names: ['first', 'second'] 281 | })) 282 | .toInt({ 283 | property: 'second' 284 | }) 285 | .output(function(err, result) { 286 | var withInt = { name: 'testData', results: { 'c1': [ { key: 1, first: 'data', second: 1}, { key: 2, first: 'data', second: 2}, { key: 3, first: 'data', second: 3 }]}}; 287 | assert.deepEqual(withInt, result); 288 | done(); 289 | }); 290 | }); 291 | }); 292 | 293 | //================================================================== 294 | // toFloat 295 | //================================================================== 296 | describe('toFloat', function() { 297 | var testData; 298 | beforeEach(function() { 299 | testData = { name: 'testData', results: { 'c1': [ { key: 1, val: "data 1.2814123"}, { key: 2, val: "data 2.2321312"}, { key: 3, val: "data 3.324" }]}}; 300 | }); 301 | 302 | it('should convert char to float', function(done) { 303 | (new KimFilter(testData)) 304 | .setCurrCollection('c1') 305 | .split({ 306 | property: 'val', 307 | separator: ' ', 308 | names: ['first', 'second'] 309 | }) 310 | .toFloat({ 311 | property: 'second', 312 | decimal: 3 313 | }) 314 | .output(function(err, result) { 315 | var withFloat = { name: 'testData', results: { 'c1': [ { key: 1, first: 'data', second: 1.281}, { key: 2, first: 'data', second: 2.232}, { key: 3, first: 'data', second: 3.324}]}}; 316 | assert.deepEqual(withFloat, result); 317 | done(); 318 | }); 319 | }); 320 | }) 321 | 322 | //================================================================== 323 | // Remove 324 | //================================================================== 325 | describe('Remove', function() { 326 | var testData; 327 | beforeEach(function() { 328 | testData = { name: 'testData', results: { 'c1': [ { key: 1, first: 'data', second: 1.281}, { key: 2, first: 'data', second: 2.232}, { key: 3, first: 'data', second: 3.324}]}}; 329 | }); 330 | 331 | it('should not remove any element' ,function() { 332 | (new KimFilter(testData)) 333 | .setCurrCollection('c1') 334 | .remove({ 335 | property: 'second', 336 | operator: '===', 337 | target: 5 338 | }) 339 | .output(function(err, result) { 340 | var filtered = { name: 'testData', results: { 'c1': [ { key: 1, first: 'data', second: 1.281}, { key: 2, first: 'data', second: 2.232}, { key: 3, first: 'data', second: 3.324}]}}; 341 | assert.deepEqual(filtered, result); 342 | done(); 343 | }); 344 | }) 345 | 346 | it('should remove element whose `val` is larger than 2', function(done) { 347 | (new KimFilter(testData)) 348 | .setCurrCollection('c1') 349 | .remove({ 350 | property: 'second', 351 | operator: '<=', 352 | target: 2 353 | }) 354 | .output(function(err, result) { 355 | var filtered = { name: 'testData', results: { 'c1': [ { key: 1, first: 'data', second: 1.281} ]}}; 356 | assert.deepEqual(filtered, result); 357 | done(); 358 | }); 359 | }); 360 | 361 | it('should remove all elements' ,function() { 362 | (new KimFilter(testData)) 363 | .setCurrCollection('c1') 364 | .remove({ 365 | property: 'second', 366 | operator: '>', 367 | target: 0 368 | }) 369 | .output(function(err, result) { 370 | var filtered = { name: 'testData', results: { 'c1': []}}; 371 | assert.deepEqual(filtered, result); 372 | done(); 373 | }); 374 | }) 375 | }); 376 | 377 | //================================================================== 378 | // RemoveProp 379 | //================================================================== 380 | describe('RemoveProp', function() { 381 | var testData; 382 | beforeEach(function() { 383 | testData = { name: 'testData', results: { 'c1': [ { key: 1, val: "data 1"}, { key: 2, val: "data 2"}, { key: 3, val: "data 3" }]}}; 384 | }); 385 | 386 | it('should not remove any properties(`property` not found)', function() { 387 | (new KimFilter(testData)) 388 | .setCurrCollection('c1') 389 | .removeProp({ 390 | properties: ['newkey'] 391 | }) 392 | .output(function(err, result) { 393 | var removed = { name: 'testData', results: { 'c1': [ { key: 1, val: "data 1"}, { key: 2, val: "data 2"}, { key: 3, val: "data 3" }]}}; 394 | assert.deepEqual(removed, result); 395 | done(); 396 | }); 397 | }); 398 | 399 | it('should remove the property `val`', function() { 400 | (new KimFilter(testData)) 401 | .setCurrCollection('c1') 402 | .removeProp({ 403 | properties: ['val'] 404 | }) 405 | .output(function(err, result) { 406 | var removed = { name: 'testData', results: { 'c1': [ { key: 1 }, { key: 2 }, { key: 3 }]}}; 407 | assert.deepEqual(removed, result); 408 | done(); 409 | }); 410 | }); 411 | 412 | it('should remove all properties.', function() { 413 | (new KimFilter(testData)) 414 | .setCurrCollection('c1') 415 | .removeProp({ 416 | properties: ['key', 'val'] 417 | }) 418 | .output(function(err, result) { 419 | var removed = { name: 'testData', results: { 'c1': [ {}, {}, {}]}}; 420 | assert.deepEqual(removed, result); 421 | done(); 422 | }); 423 | }); 424 | }); 425 | 426 | 427 | //================================================================== 428 | // renameProperty 429 | //================================================================== 430 | describe('renameProperty', function() { 431 | var testData; 432 | beforeEach(function() { 433 | testData = { name: 'testData', results: { 'c1': [ { key: 1, val: "data 1"}, { key: 2, val: "data 2"}, { key: 3, val: "data 3" }]}}; 434 | }); 435 | 436 | it('should not rename property that does not exist', function(done) { 437 | (new KimFilter(testData)) 438 | .setCurrCollection('c1') 439 | .renameProperty({ 440 | properties: ['newkey'], 441 | newnames: ['newprop'] 442 | }) 443 | .output(function(err, result) { 444 | //_.forEach(result.results.c1, function(val, key, idx) { 445 | // console.log(val); 446 | //}); 447 | var removed = { name: 'testData', results: { 'c1': [ { key: 1, val: "data 1"}, { key: 2, val: "data 2"}, { key: 3, val: "data 3" }]}}; 448 | assert.deepEqual(removed, result); 449 | done(); 450 | }); 451 | }); 452 | 453 | it('should rename `key` to `newkey`', function(done) { 454 | (new KimFilter(testData)) 455 | .setCurrCollection('c1') 456 | .renameProperty({ 457 | properties: ['key'], 458 | newnames: ['newkey'] 459 | }) 460 | .output(function(err, result) { 461 | var removed = { name: 'testData', results: { 'c1': [ { newkey: 1, val: "data 1"}, { newkey: 2, val: "data 2"}, { newkey: 3, val: "data 3" }]}}; 462 | assert.deepEqual(removed, result); 463 | done(); 464 | }); 465 | }); 466 | }); 467 | 468 | //================================================================== 469 | // renameCollection 470 | //================================================================== 471 | describe('renameCollection', function() { 472 | var testData; 473 | beforeEach(function() { 474 | testData = { name: 'testData', results: { 'c1': [ { key: 1, val: "data 1"}, { key: 2, val: "data 2"}, { key: 3, val: "data 3" }]}}; 475 | }); 476 | 477 | it('should not rename the collection which does not exist', function(done) { 478 | (new KimFilter(testData)) 479 | .renameCollection({ 480 | collection: 'c2', 481 | newname: 'c3' 482 | }) 483 | .output(function(err, result) { 484 | var removed = { name: 'testData', results: { 'c1': [ { key: 1, val: "data 1"}, { key: 2, val: "data 2"}, { key: 3, val: "data 3" }]}}; 485 | assert.deepEqual(removed, result); 486 | done(); 487 | }); 488 | }); 489 | 490 | it('should rename `c1` to `c2`', function(done) { 491 | (new KimFilter(testData)) 492 | .renameCollection({ 493 | collection: 'c1', 494 | newname: 'c2' 495 | }) 496 | .output(function(err, result) { 497 | //_.forEach(result.results.c2, function(val, key, idx) { 498 | // console.log(val); 499 | //}); 500 | var removed = { name: 'testData', results: { 'c2': [ { key: 1, val: "data 1"}, { key: 2, val: "data 2"}, { key: 3, val: "data 3" }]}}; 501 | assert.deepEqual(removed, result); 502 | done(); 503 | }); 504 | }); 505 | }); 506 | 507 | //================================================================== 508 | // Utilities 509 | //================================================================== 510 | describe('setPropByString', function() { 511 | var testData; 512 | beforeEach(function() { 513 | testData = { ID: 1, info: { title: 'Hello World', href: 'https://abc.com' }, value: "193 points" }; 514 | }); 515 | 516 | it('should not modify object if given property does not exist', function() { 517 | var copy = _.cloneDeep(testData); 518 | Util.setPropByString(copy, 'infoo.href', 'https://abc.com'); 519 | assert.deepEqual(copy, testData); 520 | }); 521 | 522 | it('should modify ID', function() { 523 | var modified = { ID: 2, info: { title: 'Hello World', href: 'https://abc.com' }, value: "193 points" }; 524 | Util.setPropByString(testData, 'ID', 2); 525 | assert.deepEqual(modified, testData); 526 | }); 527 | 528 | it('should modify info.href', function() { 529 | var modified = { ID: 1, info: { title: 'Hello World', href: 'https://xyz.com' }, value: "193 points" }; 530 | Util.setPropByString(testData, 'info.href', 'https://xyz.com'); 531 | assert.deepEqual(modified, testData); 532 | }); 533 | }); 534 | 535 | describe('getPropByString', function(){ 536 | var testData; 537 | beforeEach(function() { 538 | testData = { ID: 1, info: { title: 'Hello World', href: 'https://abc.com' }, value: "193 points" }; 539 | }); 540 | 541 | it('should get undefined if property doesn\'t exist', function() { 542 | var val = Util.getPropByString(testData, 'Id'); 543 | assert.equal(val, undefined); 544 | }); 545 | 546 | it('should get the value of ID', function() { 547 | var val = Util.getPropByString(testData, 'ID'); 548 | assert.equal(val, 1); 549 | }); 550 | 551 | it('should get the value of info.title', function() { 552 | var val = Util.getPropByString(testData, 'info.title'); 553 | assert.equal(val, 'Hello World'); 554 | }); 555 | }); 556 | 557 | describe('deletePropByString', function() { 558 | var testData; 559 | beforeEach(function() { 560 | testData = { ID: 1, info: { title: 'Hello World', href: 'https://abc.com' }, value: "193 points" }; 561 | }); 562 | 563 | it('should not modify object if property to be deleted does not exist', function() { 564 | var deleted = { ID: 1, info: { title: 'Hello World', href: 'https://abc.com' }, value: "193 points" }; 565 | Util.deletePropByString(testData, 'Id'); 566 | assert.deepEqual(deleted, testData); 567 | }); 568 | 569 | it('should delete ID', function() { 570 | var deleted = { info: { title: 'Hello World', href: 'https://abc.com' }, value: "193 points" }; 571 | Util.deletePropByString(testData, 'ID'); 572 | assert.deepEqual(testData, deleted); 573 | }); 574 | 575 | it('should delete info.href', function() { 576 | var deleted = { ID: 1, info: { title: 'Hello World' }, value: "193 points" }; 577 | Util.deletePropByString(testData, 'info.href'); 578 | assert.deepEqual(testData, deleted); 579 | }); 580 | 581 | it('should delete `info`', function() { 582 | var deleted = { ID: 1, value: "193 points" }; 583 | Util.deletePropByString(testData, 'info'); 584 | assert.deepEqual(deleted, testData); 585 | }); 586 | }) 587 | }); 588 | }); 589 | -------------------------------------------------------------------------------- /default/test.js: -------------------------------------------------------------------------------- 1 | var _ = require('lodash'); 2 | var Util = require('./Util.js'); 3 | var Q = require('q'); 4 | var request = require('request'); 5 | 6 | var KimFilter = require('../index.js'); 7 | 8 | var data = { 9 | "name": "function_filters", 10 | "count": 30, 11 | "frequency": "Manual Crawl", 12 | "version": 3, 13 | "newresults": true, 14 | "lastrunstatus": "success", 15 | "lastsuccess": "Tue Jan 06 2015 02:43:31 GMT+0000 (UTC)", 16 | "thisversionstatus": "success", 17 | "thisversionrun": "Tue Jan 06 2015 02:43:29 GMT+0000 (UTC)", 18 | "results": { 19 | "News": [ 20 | { 21 | "ID": "1.", 22 | "Title": { 23 | "href": "https://stories.californiasunday.com/2015-01-04/begich-towers-whittier-alaska/", 24 | "text": "A town of about 200 people, almost all of whom live in the same building" 25 | }, 26 | "Karma": "329 points" 27 | }, 28 | { 29 | "ID": "2.", 30 | "Title": { 31 | "href": "http://www.ifc0nfig.com/moonpig-vulnerability/", 32 | "text": "Moonpig.com Vulnerability – Exposes customer results" 33 | }, 34 | "Karma": "171 points" 35 | }, 36 | { 37 | "ID": "3.", 38 | "Title": { 39 | "href": "http://iquantny.tumblr.com/post/107245431809/how-software-in-half-of-nyc-cabs-generates-5-2", 40 | "text": "How Software in Half of NYC Cabs Generates $5.2M a Year in Extra Tips" 41 | }, 42 | "Karma": "145 points" 43 | }, 44 | { 45 | "ID": "4.", 46 | "Title": { 47 | "href": "http://www.spacetelescope.org/news/heic1502/", 48 | "text": "Hubble captures the sharpest ever view of Andromeda Galaxy" 49 | }, 50 | "Karma": "100 points" 51 | }, 52 | { 53 | "ID": "5.", 54 | "Title": { 55 | "href": "https://wit.ai/blog/2015/01/05/wit-ai-facebook", 56 | "text": "Wit.ai (YC W14) Is Joining Facebook" 57 | }, 58 | "Karma": "128 points" 59 | }, 60 | { 61 | "ID": "6.", 62 | "Title": { 63 | "href": "http://www.panic.com/blog/the-2014-panic-report/", 64 | "text": "The 2014 Panic Report" 65 | }, 66 | "Karma": "123 points" 67 | }, 68 | { 69 | "ID": "7.", 70 | "Title": { 71 | "href": "http://www.donotlick.com/2015/01/05/8-increase-in-reddit-account-registrations/", 72 | "text": "CSS changes that increased Reddit account creation by 200K yearly users" 73 | }, 74 | "Karma": "42 points" 75 | }, 76 | { 77 | "ID": "8.", 78 | "Title": { 79 | "href": "http://cactusformac.com/", 80 | "text": "Cactus for Mac – A fast, easy and free static site generator" 81 | }, 82 | "Karma": "81 points" 83 | }, 84 | { 85 | "ID": "9.", 86 | "Title": { 87 | "href": "https://www.wordsapi.com/", 88 | "text": "Show HN: WordsAPI" 89 | }, 90 | "Karma": "103 points" 91 | }, 92 | { 93 | "ID": "10.", 94 | "Title": { 95 | "href": "http://www.plosone.org/article/info:doi/10.1371/journal.pone.0083325&", 96 | "text": "Identifiable Images of Bystanders Extracted from Corneal Reflections (2013)" 97 | }, 98 | "Karma": "144 points" 99 | }, 100 | { 101 | "ID": "11.", 102 | "Title": { 103 | "href": "http://dev.stephendiehl.com/fun/", 104 | "text": "Write You a Haskell: Building a modern functional compiler from first principles" 105 | }, 106 | "Karma": "363 points" 107 | }, 108 | { 109 | "ID": "12.", 110 | "Title": { 111 | "href": "https://blog.compose.io/new-year-new-resultsbase-postgresql-on-compose/", 112 | "text": "Now available: auto-scaling PostgreSQL deployments" 113 | }, 114 | "Karma": "109 points" 115 | }, 116 | { 117 | "ID": "13.", 118 | "Title": { 119 | "href": "http://www.nytimes.com/2015/01/06/business/media/dish-network-announces-web-based-pay-tv-offering.html", 120 | "text": "Dish Network Announces Web-Based Pay TV Offering" 121 | }, 122 | "Karma": "50 points" 123 | }, 124 | { 125 | "ID": "14.", 126 | "Title": { 127 | "href": "http://hpiers.obspm.fr/iers/bul/bulc/bulletinc.dat", 128 | "text": "A positive leap second will be introduced at the end of June 2015" 129 | }, 130 | "Karma": "174 points" 131 | }, 132 | { 133 | "ID": "15.", 134 | "Title": { 135 | "href": "http://www.nytimes.com/2015/01/05/arts/writers-say-they-feel-censored-by-surveillance.html?_r=0", 136 | "text": "Writers Say They Feel Censored by Surveillance" 137 | }, 138 | "Karma": "191 points" 139 | }, 140 | { 141 | "ID": "16.", 142 | "Title": { 143 | "href": "http://www.nytimes.com/aponline/2014/12/30/us/ap-us-georgia-website-hack.html?_r=0", 144 | "text": "Georgia Tech Student Indicted in UGA Hack" 145 | }, 146 | "Karma": "70 points" 147 | }, 148 | { 149 | "ID": "17.", 150 | "Title": { 151 | "href": "http://chrome.blogspot.com/2015/01/introducing-google-cast-for-audio.html", 152 | "text": "Introducing Google Cast for audio" 153 | }, 154 | "Karma": "203 points" 155 | }, 156 | { 157 | "ID": "18.", 158 | "Title": { 159 | "href": "http://econlog.econlib.org/archives/2015/01/diversity_of_th.html", 160 | "text": "Diversity of the Mind" 161 | }, 162 | "Karma": "43 points" 163 | }, 164 | { 165 | "ID": "19.", 166 | "Title": { 167 | "href": "http://www.bloomberg.com/news/2015-01-05/verizon-said-to-approach-aol-about-possible-takeover-or-venture.html", 168 | "text": "Verizon Said to Approach AOL About Possible Takeover" 169 | }, 170 | "Karma": "12 points" 171 | }, 172 | { 173 | "ID": "20.", 174 | "Title": { 175 | "href": "https://github.com/philipwalton/flexbugs/", 176 | "text": "Flexbugs: a list of flexbox bugs and cross-browser solutions to them" 177 | }, 178 | "Karma": "3 points" 179 | }, 180 | { 181 | "ID": "21.", 182 | "Title": { 183 | "href": "https://news.ycombinator.com/item?id=8842298", 184 | "text": "Teespring (YC W13) Is Hiring a Senior UI/Front-End Engineer" 185 | }, 186 | "Karma": "" 187 | }, 188 | { 189 | "ID": "22.", 190 | "Title": { 191 | "href": "http://arstechnica.com/gaming/2015/01/pokemon-plays-twitch-how-a-robot-got-irc-running-on-an-unmodified-snes/", 192 | "text": "Pokémon plays Twitch: How a robot got IRC running on an unmodified SNES" 193 | }, 194 | "Karma": "117 points" 195 | }, 196 | { 197 | "ID": "23.", 198 | "Title": { 199 | "href": "https://chatsecure.org/blog/chatsecure-ios-v3-released/", 200 | "text": "Show HN: ChatSecure iOS v3.0 (OTR + Tor + XMPP)" 201 | }, 202 | "Karma": "59 points" 203 | }, 204 | { 205 | "ID": "24.", 206 | "Title": { 207 | "href": "http://archive.wired.com/wired/archive/1.04/gibson.html", 208 | "text": "Disneyland with the Death Penalty (1993)" 209 | }, 210 | "Karma": "100 points" 211 | }, 212 | { 213 | "ID": "25.", 214 | "Title": { 215 | "href": "http://www.wired.com/2012/10/ff-why-products-fail/all/", 216 | "text": "Why Things Fail: From Tires to Helicopter Blades (2012)" 217 | }, 218 | "Karma": "79 points" 219 | }, 220 | { 221 | "ID": "26.", 222 | "Title": { 223 | "href": "http://www.economist.com/news/briefing/21637355-freelance-workers-available-moments-notice-will-reshape-nature-companies-and", 224 | "text": "The future of work: There’s an app for that" 225 | }, 226 | "Karma": "59 points" 227 | }, 228 | { 229 | "ID": "27.", 230 | "Title": { 231 | "href": "https://www.storyworth.com", 232 | "text": "Story Worth – privately collect and share family stories" 233 | }, 234 | "Karma": "26 points" 235 | }, 236 | { 237 | "ID": "28.", 238 | "Title": { 239 | "href": "http://lispblog.xach.com/post/107215169193/corman-lisp-sources-are-now-available", 240 | "text": "Corman Lisp sources are now available" 241 | }, 242 | "Karma": "158 points" 243 | }, 244 | { 245 | "ID": "29.", 246 | "Title": { 247 | "href": "http://www.gq.com/news-politics/201501/chinas-richest", 248 | "text": "The Bling Dynasty – The Nouveau Riche in China" 249 | }, 250 | "Karma": "29 points" 251 | }, 252 | { 253 | "ID": "30.", 254 | "Title": { 255 | "href": "https://www.youtube.com/watch?v=XNqOU4jx62I", 256 | "text": "Onewheel [video]" 257 | }, 258 | "Karma": "66 points" 259 | } 260 | ] 261 | } 262 | }; 263 | 264 | var data_bypage = { 265 | "name": "function_filters", 266 | "count": 120, 267 | "frequency": "Manual Crawl", 268 | "version": 8, 269 | "newresults": true, 270 | "lastrunstatus": "success", 271 | "lastsuccess": "Fri Jan 09 2015 19:52:30 GMT+0000 (UTC)", 272 | "thisversionstatus": "success", 273 | "thisversionrun": "Fri Jan 09 2015 19:52:19 GMT+0000 (UTC)", 274 | "results": [ 275 | { 276 | "page": "1", 277 | "url": "https://news.ycombinator.com/news?p=1", 278 | "News": [ 279 | { 280 | "ID": "1", 281 | "Title": { 282 | "href": "http://blog.rust-lang.org/2015/01/09/Rust-1.0-alpha.html", 283 | "text": "Announcing Rust 1.0 Alpha" 284 | }, 285 | "Karma": "294 points" 286 | }, 287 | { 288 | "ID": "2", 289 | "Title": { 290 | "href": "http://www.johnskylar.com/post/107416685924/a-career-in-science-will-cost-you-your-firstborn", 291 | "text": "A Career in Science Will Cost You Your Firstborn" 292 | }, 293 | "Karma": "189 points" 294 | }, 295 | { 296 | "ID": "3", 297 | "Title": { 298 | "href": "http://thorconpower.com/", 299 | "text": "ThorCon Power" 300 | }, 301 | "Karma": "70 points" 302 | }, 303 | { 304 | "ID": "4", 305 | "Title": { 306 | "href": "http://news.stanford.edu/news/2014/december/ai-century-study-121614.html", 307 | "text": "Stanford to host 100-year study on artificial intelligence" 308 | }, 309 | "Karma": "39 points" 310 | }, 311 | { 312 | "ID": "5", 313 | "Title": { 314 | "href": "http://grack.com/blog/2015/01/09/abusing-css3-selectors/", 315 | "text": "Abusing CSS3's nth-child selector to invent new ones" 316 | }, 317 | "Karma": "34 points" 318 | }, 319 | { 320 | "ID": "6", 321 | "Title": { 322 | "href": "http://krebsonsecurity.com/2015/01/lizard-stresser-runs-on-hacked-home-routers/", 323 | "text": "Lizard Stresser Runs on Hacked Home Routers" 324 | }, 325 | "Karma": "67 points" 326 | }, 327 | { 328 | "ID": "7", 329 | "Title": { 330 | "href": "http://www.braveclojure.com/core-async/", 331 | "text": "Master Concurrent Processes with core.async" 332 | }, 333 | "Karma": "93 points" 334 | }, 335 | { 336 | "ID": "8", 337 | "Title": { 338 | "href": "https://newscientist.com/article/dn26753?cmpid=NLC%7CNSNS%7C2015-0108-GLOBAL", 339 | "text": "Mathematician's anger over his unread 500-page proof" 340 | }, 341 | "Karma": "38 points" 342 | }, 343 | { 344 | "ID": "9", 345 | "Title": { 346 | "href": "http://arstechnica.com/science/2015/01/supermassive-black-hole-binary-discovered/", 347 | "text": "Supermassive Black Hole Binary Discovered" 348 | }, 349 | "Karma": "38 points" 350 | }, 351 | { 352 | "ID": "10", 353 | "Title": { 354 | "href": "https://github.com/prakhar1989/awesome-courses", 355 | "text": "List of University Courses for Learning Computer Science" 356 | }, 357 | "Karma": "231 points" 358 | }, 359 | { 360 | "ID": "11", 361 | "Title": { 362 | "href": "http://modong.github.io/pcc-page", 363 | "text": "PCC: Performance-oriented Congestion Control" 364 | }, 365 | "Karma": "48 points" 366 | }, 367 | { 368 | "ID": "12", 369 | "Title": { 370 | "href": "http://blog.fogcreek.com/increase-defect-detection-with-our-code-review-checklist-example", 371 | "text": "Code Review Checklist" 372 | }, 373 | "Karma": "142 points" 374 | }, 375 | { 376 | "ID": "13", 377 | "Title": { 378 | "href": "http://www.wsj.com/articles/box-inc-expects-to-raise-up-to-186-9-million-in-ipo-1420805949", 379 | "text": "Box Moves Ahead With IPO" 380 | }, 381 | "Karma": "21 points" 382 | }, 383 | { 384 | "ID": "14", 385 | "Title": { 386 | "href": "http://2ld.de/edidoom/", 387 | "text": "Intel Edison-based video game console playing Doom" 388 | }, 389 | "Karma": "28 points" 390 | }, 391 | { 392 | "ID": "15", 393 | "Title": { 394 | "href": "http://enjoythemusic.com/hificritic/vol5_no3/listening_to_storage.htm", 395 | "text": "Significant sound quality differences between digital music storage technologies" 396 | }, 397 | "Karma": "16 points" 398 | }, 399 | { 400 | "ID": "16", 401 | "Title": { 402 | "href": "http://www.businessweek.com/articles/2015-01-08/takadu-helps-israel-be-a-most-efficient-water-manager#r=hp-lst", 403 | "text": "Israel's water ninja" 404 | }, 405 | "Karma": "14 points" 406 | }, 407 | { 408 | "ID": "17", 409 | "Title": { 410 | "href": "http://solsticlipse.com/2015/01/09/intel-real-sense-camera-on-linux.html", 411 | "text": "Intel RealSense camera on Linux" 412 | }, 413 | "Karma": "16 points" 414 | }, 415 | { 416 | "ID": "18", 417 | "Title": { 418 | "href": "https://news.ycombinator.com/item?id=8862542", 419 | "text": "What does it take to run a web app with 5K – 10K users?" 420 | }, 421 | "Karma": "120 points" 422 | }, 423 | { 424 | "ID": "19", 425 | "Title": { 426 | "href": "http://techeffigytutorials.blogspot.com/2015/01/markov-chains-explained.html", 427 | "text": "Markov Chains Explained" 428 | }, 429 | "Karma": "19 points" 430 | }, 431 | { 432 | "ID": "20", 433 | "Title": { 434 | "href": "http://blogs.wsj.com/digits/2015/01/08/google-wants-to-sell-you-auto-insurance/", 435 | "text": "Google Wants to Sell You Auto Insurance" 436 | }, 437 | "Karma": "13 points" 438 | }, 439 | { 440 | "ID": "21", 441 | "Title": { 442 | "href": "https://github.com/unbit/spockfs", 443 | "text": "SpockFS – An HTTP-based network filesystem" 444 | }, 445 | "Karma": "10 points" 446 | }, 447 | { 448 | "ID": "22", 449 | "Title": { 450 | "href": "http://peertopeer.io/", 451 | "text": "Peer to Peer – Hone your skills by watching live coding videos" 452 | }, 453 | "Karma": "25 points" 454 | }, 455 | { 456 | "ID": "23", 457 | "Title": { 458 | "href": "https://ginnabaker.wordpress.com/2014/12/06/nothing-you-can-do-impresses-me/", 459 | "text": "“Nothing you can do impresses me”" 460 | }, 461 | "Karma": "125 points" 462 | }, 463 | { 464 | "ID": "24", 465 | "Title": { 466 | "href": "http://azure.microsoft.com/blog/2015/01/08/azure-is-now-bigger-faster-more-open-and-more-secure/", 467 | "text": "Azure is now bigger, faster, more open, and more secure – Microsoft Azure Blog" 468 | }, 469 | "Karma": "3 points" 470 | }, 471 | { 472 | "ID": "25", 473 | "Title": { 474 | "href": "https://www.acritelli.com/hacking-voip-decrypting-sdes-protected-srtp-phone-calls/", 475 | "text": "Decrypting SDES Protected SRTP Phone Calls" 476 | }, 477 | "Karma": "8 points" 478 | }, 479 | { 480 | "ID": "26", 481 | "Title": { 482 | "href": "http://blog.codecombat.com/codecombat-is-hiring-chief-artisan", 483 | "text": "CodeCombat (YC W14) is hiring a Chief Artisan (level builder)" 484 | }, 485 | "Karma": "" 486 | }, 487 | { 488 | "ID": "27", 489 | "Title": { 490 | "href": "http://www.newyorker.com/news/news-desk/battle-new-orleans-birthed-american-democracy", 491 | "text": "How the Battle of New Orleans Birthed the American Character" 492 | }, 493 | "Karma": "6 points" 494 | }, 495 | { 496 | "ID": "28", 497 | "Title": { 498 | "href": "http://pipeline.corante.com/archives/2015/01/08/teixobactin_a_new_antibiotic_from_a_new_platform.php", 499 | "text": "Teixobactin: A New Antibiotic from a New Platform?" 500 | }, 501 | "Karma": "15 points" 502 | }, 503 | { 504 | "ID": "29", 505 | "Title": { 506 | "href": "http://cires.colorado.edu/news/press/2014/crowdsourcingscience.html?", 507 | "text": "Crowdsourcing Earth's magnetic field" 508 | }, 509 | "Karma": "11 points" 510 | }, 511 | { 512 | "ID": "30", 513 | "Title": { 514 | "href": "http://nautil.us/issue/20/creativity/the-strange-inevitability-of-evolution", 515 | "text": "The Strange Inevitability of Evolution" 516 | }, 517 | "Karma": "7 points" 518 | } 519 | ] 520 | }, 521 | { 522 | "page": "2", 523 | "url": "https://news.ycombinator.com/news?p=2", 524 | "News": [ 525 | { 526 | "ID": "31", 527 | "Title": { 528 | "href": "http://www.scientificamerican.com/article/for-sale-your-name-here-in-a-prestigious-science-journal/", 529 | "text": "For Sale: “Your Name Here” in a Prestigious Science Journal" 530 | }, 531 | "Karma": "42 points" 532 | }, 533 | { 534 | "ID": "32", 535 | "Title": { 536 | "href": "http://www.latimes.com/science/sciencenow/la-sci-sn-beethoven-heartbeat-20150108-story.html?track=lat-email-healthreport", 537 | "text": "Is arrhythmia at the heart of Beethoven's music?" 538 | }, 539 | "Karma": "10 points" 540 | }, 541 | { 542 | "ID": "33", 543 | "Title": { 544 | "href": "https://blog.whitehatsec.com/north-koreas-naenara-web-browser-its-weirder-than-we-thought/", 545 | "text": "North Korea’s Naenara Web Browser: It’s Weirder Than We Thought" 546 | }, 547 | "Karma": "104 points" 548 | }, 549 | { 550 | "ID": "34", 551 | "Title": { 552 | "href": "http://community.wolfram.com/groups/-/m/t/418720", 553 | "text": "Perfectly centered break of a perfectly aligned pool ball rack" 554 | }, 555 | "Karma": "5 points" 556 | }, 557 | { 558 | "ID": "35", 559 | "Title": { 560 | "href": "http://tachyus.com/joinus/senior-front-end-engineer.html", 561 | "text": "TACHYUS Hiring Senior Front-End Engineer (JavaScript)" 562 | }, 563 | "Karma": "4 points" 564 | }, 565 | { 566 | "ID": "36", 567 | "Title": { 568 | "href": "http://languagelog.ldc.upenn.edu/nll/?p=16938", 569 | "text": "In the 20th century, the frequency of the definite article “the” decreased" 570 | }, 571 | "Karma": "8 points" 572 | }, 573 | { 574 | "ID": "37", 575 | "Title": { 576 | "href": "http://zephyrosanemos.com", 577 | "text": "Terrain Rendering and GUIs with WebGL" 578 | }, 579 | "Karma": "116 points" 580 | }, 581 | { 582 | "ID": "38", 583 | "Title": { 584 | "href": "https://www.reddit.com/r/Bitcoin/comments/2rrxq7/on_why_010s_release_notes_say_we_have_reason_to/", 585 | "text": "Why Bitcoin Core 0.10's release notes say “…libsecp256k1 is better than…OpenSSL”" 586 | }, 587 | "Karma": "131 points" 588 | }, 589 | { 590 | "ID": "39", 591 | "Title": { 592 | "href": "http://www.latimes.com/food/dailydish/la-dd-dont-soak-dried-beans-20140911-story.html", 593 | "text": "Don't soak your dried beans" 594 | }, 595 | "Karma": "50 points" 596 | }, 597 | { 598 | "ID": "40", 599 | "Title": { 600 | "href": "https://savannah.gnu.org/forum/forum.php?forum_id=8175", 601 | "text": "GNU Guix ported to ARM and other niceties of the new year" 602 | }, 603 | "Karma": "53 points" 604 | }, 605 | { 606 | "ID": "41", 607 | "Title": { 608 | "href": "http://philip.greenspun.com/careers/women-in-science?hn", 609 | "text": "Women in Science" 610 | }, 611 | "Karma": "4 points" 612 | }, 613 | { 614 | "ID": "42", 615 | "Title": { 616 | "href": "http://medicalxpress.com/news/2015-01-human-brain-memories-tidy-pruning.html", 617 | "text": "Human brain keeps memories tidy by pruning inaccurate ones" 618 | }, 619 | "Karma": "11 points" 620 | }, 621 | { 622 | "ID": "43", 623 | "Title": { 624 | "href": "http://blogs.wsj.com/indiarealtime/2015/01/07/indias-first-openly-transgender-mayor-in-her-own-words/", 625 | "text": "India's First Openly Transgender Mayor in Her Own Words" 626 | }, 627 | "Karma": "30 points" 628 | }, 629 | { 630 | "ID": "44", 631 | "Title": { 632 | "href": "http://www.andrewoswald.com/docs/entrepre.pdf", 633 | "text": "What makes an entrepreneur? (1998) [pdf]" 634 | }, 635 | "Karma": "6 points" 636 | }, 637 | { 638 | "ID": "45", 639 | "Title": { 640 | "href": "http://blog.codinghorror.com/the-god-login/", 641 | "text": "The God Login" 642 | }, 643 | "Karma": "170 points" 644 | }, 645 | { 646 | "ID": "46", 647 | "Title": { 648 | "href": "http://www.wired.com/2015/01/architecture-and-vision-warkawater/", 649 | "text": "A Bamboo Tower That Produces Water from Air" 650 | }, 651 | "Karma": "8 points" 652 | }, 653 | { 654 | "ID": "47", 655 | "Title": { 656 | "href": "http://blog.omarduarte.me/stuff-every-junior-developer-should-know/", 657 | "text": "Stuff Every Junior Developer Should Know" 658 | }, 659 | "Karma": "7 points" 660 | }, 661 | { 662 | "ID": "48", 663 | "Title": { 664 | "href": "https://jobs.lever.co/kamcord/8ce5e4ab-4718-4310-a3a9-1e3e357a22cf?lever-source=hackernews", 665 | "text": "Kamcord is looking for a Lead Designer" 666 | }, 667 | "Karma": "" 668 | }, 669 | { 670 | "ID": "49", 671 | "Title": { 672 | "href": "http://www.kellegous.com/j/2015/01/09/beware-comcast-business/", 673 | "text": "Don't Lose $2,000 to Comcast Business" 674 | }, 675 | "Karma": "4 points" 676 | }, 677 | { 678 | "ID": "50", 679 | "Title": { 680 | "href": "http://www.catb.org/~esr/faqs/hacker-howto.html", 681 | "text": "How to Become a Hacker (2001)" 682 | }, 683 | "Karma": "64 points" 684 | }, 685 | { 686 | "ID": "51", 687 | "Title": { 688 | "href": "http://brookeallen.com/pages/archives/1234", 689 | "text": "How my life was changed when I began caring about the people I did not hire" 690 | }, 691 | "Karma": "563 points" 692 | }, 693 | { 694 | "ID": "52", 695 | "Title": { 696 | "href": "http://thume.ca/screentunes/", 697 | "text": "On some LCD monitors this will cause them to emit a tone (epilepsy warning)" 698 | }, 699 | "Karma": "132 points" 700 | }, 701 | { 702 | "ID": "53", 703 | "Title": { 704 | "href": "http://www.askapache.com/hacking/ping-unix-darpa-muuss.html#ping_story_Ive_heard", 705 | "text": "The best ping story I've ever heard" 706 | }, 707 | "Karma": "120 points" 708 | }, 709 | { 710 | "ID": "54", 711 | "Title": { 712 | "href": "https://news.ycombinator.com/item?id=8863782", 713 | "text": "What are some good technology projects to donate to in 2015?" 714 | }, 715 | "Karma": "4 points" 716 | }, 717 | { 718 | "ID": "55", 719 | "Title": { 720 | "href": "http://www.3cosystem.com", 721 | "text": "Show HN: 3cosystem – biggest tech and event calendar in your city" 722 | }, 723 | "Karma": "6 points" 724 | }, 725 | { 726 | "ID": "56", 727 | "Title": { 728 | "href": "https://www.youtube.com/watch?v=ubaX1Smg6pY&=", 729 | "text": "Is it really 'Complex'? Or did we just make it 'Complicated'? [video]" 730 | }, 731 | "Karma": "216 points" 732 | }, 733 | { 734 | "ID": "57", 735 | "Title": { 736 | "href": "http://www.businessinsider.com/charlize-theron-10-million-huntsman-paycheck-2015-1", 737 | "text": "Charlize Theron Negotiated a $10MM Paycheck After Sony Hack Revealed Unequal Pay" 738 | }, 739 | "Karma": "5 points" 740 | }, 741 | { 742 | "ID": "58", 743 | "Title": { 744 | "href": "http://www.citylab.com/work/2015/01/americas-best-performing-cities-in-2014/384336/", 745 | "text": "America's Best Performing Cities in 2014" 746 | }, 747 | "Karma": "4 points" 748 | }, 749 | { 750 | "ID": "59", 751 | "Title": { 752 | "href": "https://lists.gnu.org/archive/html/emacs-devel/2015-01/msg00171.html", 753 | "text": "Lead Emacs developer considering forking over GCC and AST issues" 754 | }, 755 | "Karma": "29 points" 756 | }, 757 | { 758 | "ID": "60", 759 | "Title": { 760 | "href": "http://joachimesque.com/globe/#1", 761 | "text": "Le Paper Globe: a do-it-yourself terrestrial globe" 762 | }, 763 | "Karma": "36 points" 764 | } 765 | ] 766 | }, 767 | { 768 | "page": "3", 769 | "url": "https://news.ycombinator.com/news?p=3", 770 | "News": [ 771 | { 772 | "ID": "61", 773 | "Title": { 774 | "href": "http://blog.rescale.com/which-one-to-worry-about-ebola-vs-influenza/", 775 | "text": "Infection Simulation: Ebola vs. Influenza" 776 | }, 777 | "Karma": "5 points" 778 | }, 779 | { 780 | "ID": "62", 781 | "Title": { 782 | "href": "http://www.cs.unm.edu/~dlchao/flake/doom/", 783 | "text": "Doom as a tool for system administration (1999)" 784 | }, 785 | "Karma": "194 points" 786 | }, 787 | { 788 | "ID": "63", 789 | "Title": { 790 | "href": "https://twitter.com/TcitWorld/status/553530506453987328/photo/1", 791 | "text": "Whois on any .fr domain displays “je suis charlie”" 792 | }, 793 | "Karma": "121 points" 794 | }, 795 | { 796 | "ID": "64", 797 | "Title": { 798 | "href": "http://sideprojects.assembly.com", 799 | "text": "Show HN: Save your side project" 800 | }, 801 | "Karma": "172 points" 802 | }, 803 | { 804 | "ID": "65", 805 | "Title": { 806 | "href": "http://dspace.mit.edu/bitstream/handle/1721.1/5731/AIM-514.pdf", 807 | "text": "Design of Lisp-Based Processors Or, LAMBDA: The Ultimate Opcode (1979) [pdf]" 808 | }, 809 | "Karma": "82 points" 810 | }, 811 | { 812 | "ID": "66", 813 | "Title": { 814 | "href": "http://www.forbes.com/sites/nathanvardi/2014/12/01/the-king-of-online-gambling-is-34/", 815 | "text": "The King of Online Gambling" 816 | }, 817 | "Karma": "35 points" 818 | }, 819 | { 820 | "ID": "67", 821 | "Title": { 822 | "href": "https://www.youtube.com/watch?v=g_ULtNYRCbg", 823 | "text": "Redstone Word Processor in Minecraft [video]" 824 | }, 825 | "Karma": "159 points" 826 | }, 827 | { 828 | "ID": "68", 829 | "Title": { 830 | "href": "https://news.ycombinator.com/item?id=8861587", 831 | "text": "Transcriptic, a robotic cloud biology lab, is hiring scientists and engineers" 832 | }, 833 | "Karma": "" 834 | }, 835 | { 836 | "ID": "69", 837 | "Title": { 838 | "href": "http://www.scrollslowhavefun.com/", 839 | "text": "Scroll Slow. Have Fun" 840 | }, 841 | "Karma": "541 points" 842 | }, 843 | { 844 | "ID": "70", 845 | "Title": { 846 | "href": "http://bjoern.brembs.net/2015/01/booming-university-administrations/", 847 | "text": "The Growth of Administrative Staff in Universities" 848 | }, 849 | "Karma": "134 points" 850 | }, 851 | { 852 | "ID": "71", 853 | "Title": { 854 | "href": "http://iamjwal.com/the-tough-decision-to-leave-the-classroom/", 855 | "text": "The Tough Decision to Leave the Classroom" 856 | }, 857 | "Karma": "302 points" 858 | }, 859 | { 860 | "ID": "72", 861 | "Title": { 862 | "href": "http://raidersec.blogspot.com/2013/06/how-browsers-store-your-passwords-and.html?m=1", 863 | "text": "How Browsers Store Your Passwords (and Why You Shouldn't Let Them)" 864 | }, 865 | "Karma": "7 points" 866 | }, 867 | { 868 | "ID": "73", 869 | "Title": { 870 | "href": "http://makezine.com/2015/01/08/voxel8-demonstrates-its-electronics-capable-3d-printer-at-ces-2015/", 871 | "text": "Voxel8 Demonstrates Its Electronics-Capable 3D Printer at CES 2015 [video]" 872 | }, 873 | "Karma": "43 points" 874 | }, 875 | { 876 | "ID": "74", 877 | "Title": { 878 | "href": "http://www.aaronkharris.com/someone-else-had-your-idea-first", 879 | "text": "Someone else had your idea first" 880 | }, 881 | "Karma": "108 points" 882 | }, 883 | { 884 | "ID": "75", 885 | "Title": { 886 | "href": "https://www.eff.org/deeplinks/2015/01/wake-charlie-hebdo-attack-lets-not-sacrifice-even-more-rights", 887 | "text": "In Wake of Charlie Hebdo Attack, Let’s Not Sacrifice Even More Rights" 888 | }, 889 | "Karma": "275 points" 890 | }, 891 | { 892 | "ID": "76", 893 | "Title": { 894 | "href": "http://qz.com/311832/hacked-emails-reveal-chinas-elaborate-and-absurd-internet-propaganda-machine/", 895 | "text": "Hacked emails reveal China’s Internet propaganda machine" 896 | }, 897 | "Karma": "132 points" 898 | }, 899 | { 900 | "ID": "77", 901 | "Title": { 902 | "href": "http://www.blinkenlights.com/pc.shtml", 903 | "text": "What was the first personal computer? (1999)" 904 | }, 905 | "Karma": "66 points" 906 | }, 907 | { 908 | "ID": "78", 909 | "Title": { 910 | "href": "https://news.ycombinator.com/item?id=8863588", 911 | "text": "Ask HN: What does Engineering culture in a super professional company look like?" 912 | }, 913 | "Karma": "3 points" 914 | }, 915 | { 916 | "ID": "79", 917 | "Title": { 918 | "href": "http://www.simonowens.net/how-reddit-created-the-worlds-largest-dialogue-between-scientists-and-the-general-public/", 919 | "text": "How Reddit Sparked a Dialogue Between Scientists and the General Public" 920 | }, 921 | "Karma": "68 points" 922 | }, 923 | { 924 | "ID": "80", 925 | "Title": { 926 | "href": "http://www.theguardian.com/business/2015/jan/08/cannabis-investor-peter-theil-paypal-founder", 927 | "text": "Peter Thiel becomes marijuana's first big investor" 928 | }, 929 | "Karma": "127 points" 930 | }, 931 | { 932 | "ID": "81", 933 | "Title": { 934 | "href": "https://firstlook.org/theintercept/2015/01/09/solidarity-charlie-hebdo-cartoons/", 935 | "text": "In Solidarity with a Free Press: Some More Blasphemous Cartoons" 936 | }, 937 | "Karma": "5 points" 938 | }, 939 | { 940 | "ID": "82", 941 | "Title": { 942 | "href": "http://www.businessweek.com/articles/2015-01-07/amazon-vs-dot-jet-dot-com-marc-lore-aims-to-beat-bezos", 943 | "text": "Amazon vs. Jet.com" 944 | }, 945 | "Karma": "96 points" 946 | }, 947 | { 948 | "ID": "83", 949 | "Title": { 950 | "href": "http://www.latimes.com/business/technology/la-fi-tn-fcc-chairman-wheeler-ces-net-neutrality-title-ii-20150107-story.html?track=rss&utm_source=dlvr.it&utm_medium=twitter&dlvrit=515009", 951 | "text": "FCC chairman tips his hand on net neutrality" 952 | }, 953 | "Karma": "117 points" 954 | }, 955 | { 956 | "ID": "84", 957 | "Title": { 958 | "href": "http://www.gamasutra.com/view/feature/4111/dirty_coding_tricks.php?print=1", 959 | "text": "Dirty Coding Tricks" 960 | }, 961 | "Karma": "291 points" 962 | }, 963 | { 964 | "ID": "85", 965 | "Title": { 966 | "href": "http://techgage.com/news/samsung-unveils-first-pcie-3-0-x4-based-m-2-ssd-delivering-speeds-of-over-2gbs/", 967 | "text": "Samsung Unveils SSD Delivering Speeds of Over 2 GB/s" 968 | }, 969 | "Karma": "267 points" 970 | }, 971 | { 972 | "ID": "86", 973 | "Title": { 974 | "href": "http://applicative.acm.org/index.html", 975 | "text": "Applicative by the ACM" 976 | }, 977 | "Karma": "18 points" 978 | }, 979 | { 980 | "ID": "87", 981 | "Title": { 982 | "href": "http://richardg867.wordpress.com/2015/01/01/notes-on-red-star-os-3-0/", 983 | "text": "Notes on Red Star OS 3.0" 984 | }, 985 | "Karma": "96 points" 986 | }, 987 | { 988 | "ID": "88", 989 | "Title": { 990 | "href": "http://www.engadget.com/2015/01/05/energous-wattup-wireless-charging-demo/", 991 | "text": "Router can power your devices wirelessly from 15 feet away" 992 | }, 993 | "Karma": "64 points" 994 | }, 995 | { 996 | "ID": "89", 997 | "Title": { 998 | "href": "http://secupwn.github.io/Android-IMSI-Catcher-Detector/", 999 | "text": "Android IMSI-Catcher Detector (AIMSICD)" 1000 | }, 1001 | "Karma": "3 points" 1002 | }, 1003 | { 1004 | "ID": "90", 1005 | "Title": { 1006 | "href": "http://freesound.org/", 1007 | "text": "Freesound: A collaborative resultsbase of Creative Commons-licensed sounds" 1008 | }, 1009 | "Karma": "51 points" 1010 | } 1011 | ] 1012 | }, 1013 | { 1014 | "page": "4", 1015 | "url": "https://news.ycombinator.com/news?p=4", 1016 | "News": [ 1017 | { 1018 | "ID": "91", 1019 | "Title": { 1020 | "href": "http://www.livinstudio.com/farm432/", 1021 | "text": "Farm 432" 1022 | }, 1023 | "Karma": "249 points" 1024 | }, 1025 | { 1026 | "ID": "92", 1027 | "Title": { 1028 | "href": "http://www.noshortageofwork.com/pages/4078", 1029 | "text": "How to write if you cannot concentrate" 1030 | }, 1031 | "Karma": "16 points" 1032 | }, 1033 | { 1034 | "ID": "93", 1035 | "Title": { 1036 | "href": "http://www.healthline.com/health-news/ms-patients-who-received-stem-cell-transplants-still-in-remission-010715", 1037 | "text": "Most MS Patients Who Got Stem Cell Transplants Still in Remission Years Later" 1038 | }, 1039 | "Karma": "121 points" 1040 | }, 1041 | { 1042 | "ID": "94", 1043 | "Title": { 1044 | "href": "http://mobility-labs.com/2015/how-parents-community-groups-use-results", 1045 | "text": "How Parents and Community Groups Use Data" 1046 | }, 1047 | "Karma": "6 points" 1048 | }, 1049 | { 1050 | "ID": "95", 1051 | "Title": { 1052 | "href": "https://www.openssl.org/news/secadv_20150108.txt", 1053 | "text": "OpenSSL Security Advisory" 1054 | }, 1055 | "Karma": "94 points" 1056 | }, 1057 | { 1058 | "ID": "96", 1059 | "Title": { 1060 | "href": "https://3drealms.com/news/3d-realms-vault-1994-design-tips-tom-hall-part-1/", 1061 | "text": "Game Design Tips from Tom Hall (1994)" 1062 | }, 1063 | "Karma": "70 points" 1064 | }, 1065 | { 1066 | "ID": "97", 1067 | "Title": { 1068 | "href": "http://pokemon.winrar.io", 1069 | "text": "Show HN: Twilio plays Pokemon - Play pokemon via SMS using twilio" 1070 | }, 1071 | "Karma": "9 points" 1072 | }, 1073 | { 1074 | "ID": "98", 1075 | "Title": { 1076 | "href": "http://phys.org/news/2015-01-super-insulated-indoor.html", 1077 | "text": "Super-insulated clothing could eliminate need for indoor heating" 1078 | }, 1079 | "Karma": "48 points" 1080 | }, 1081 | { 1082 | "ID": "99", 1083 | "Title": { 1084 | "href": "https://github.com/FredKSchott/CoVim", 1085 | "text": "CoVim – Collaborative Editing for Vim" 1086 | }, 1087 | "Karma": "11 points" 1088 | }, 1089 | { 1090 | "ID": "100", 1091 | "Title": { 1092 | "href": "https://news.ycombinator.com/item?id=8856411", 1093 | "text": "Ask HN: What will be “uber-ified” next?" 1094 | }, 1095 | "Karma": "15 points" 1096 | }, 1097 | { 1098 | "ID": "101", 1099 | "Title": { 1100 | "href": "https://www.reddit.com/r/IAmA/comments/2rgsan/i_am_elon_musk_ceocto_of_a_rocket_company_ama/", 1101 | "text": "Elon Musk AMA" 1102 | }, 1103 | "Karma": "566 points" 1104 | }, 1105 | { 1106 | "ID": "102", 1107 | "Title": { 1108 | "href": "http://arstechnica.com/science/2015/01/researchers-create-quantum-memory-thats-stable-for-six-hours/", 1109 | "text": "Researchers create quantum memory that’s stable for six hours" 1110 | }, 1111 | "Karma": "5 points" 1112 | }, 1113 | { 1114 | "ID": "103", 1115 | "Title": { 1116 | "href": "http://www.theverge.com/2015/1/7/7508651/leap-second-2015-earths-rotation-slowing", 1117 | "text": "2015 is getting an extra second and that's a bit of a problem for the internet" 1118 | }, 1119 | "Karma": "4 points" 1120 | }, 1121 | { 1122 | "ID": "104", 1123 | "Title": { 1124 | "href": "http://blog.norsecorp.com/2014/12/29/ex-employee-five-others-fingered-in-sony-hack/", 1125 | "text": "Norse Investigation Focusing on a Small Group, Including Sony Ex-Employees" 1126 | }, 1127 | "Karma": "3 points" 1128 | }, 1129 | { 1130 | "ID": "105", 1131 | "Title": { 1132 | "href": "http://www.thedailybeast.com/articles/2014/12/24/no-north-korea-didn-t-hack-sony.html", 1133 | "text": "“No, North Korea Didn’t Hack Sony”" 1134 | }, 1135 | "Karma": "75 points" 1136 | }, 1137 | { 1138 | "ID": "106", 1139 | "Title": { 1140 | "href": "http://qz.com/312537/the-secret-to-the-uber-economy-is-wealth-inequality/", 1141 | "text": "The secret to the Uber economy is wealth inequality" 1142 | }, 1143 | "Karma": "11 points" 1144 | }, 1145 | { 1146 | "ID": "107", 1147 | "Title": { 1148 | "href": "http://zinc.rs/", 1149 | "text": "Zinc – Rust’s safety features applied to embedded development" 1150 | }, 1151 | "Karma": "147 points" 1152 | }, 1153 | { 1154 | "ID": "108", 1155 | "Title": { 1156 | "href": "https://news.ycombinator.com/item?id=8863172", 1157 | "text": "What effect would the reclass of ISP have on privacy (un)lawful results collection?" 1158 | }, 1159 | "Karma": "4 points" 1160 | }, 1161 | { 1162 | "ID": "109", 1163 | "Title": { 1164 | "href": "http://www.bbc.co.uk/news/science-environment-30718558", 1165 | "text": "Computer program 'perfect at poker'" 1166 | }, 1167 | "Karma": "7 points" 1168 | }, 1169 | { 1170 | "ID": "110", 1171 | "Title": { 1172 | "href": "http://www.dignited.com/11641/facebook-opens-office-south-africa-first-africa/", 1173 | "text": "Facebook opens their first office in Africa" 1174 | }, 1175 | "Karma": "3 points" 1176 | }, 1177 | { 1178 | "ID": "111", 1179 | "Title": { 1180 | "href": "https://medium.com/productivity-in-the-cloud/6-links-that-will-show-you-what-google-knows-about-you-f39b8af9decc", 1181 | "text": "6 links that will show you what Google knows about you" 1182 | }, 1183 | "Karma": "4 points" 1184 | }, 1185 | { 1186 | "ID": "112", 1187 | "Title": { 1188 | "href": "http://www.telegraph.co.uk/news/worldnews/europe/france/11335676/Hacktivists-Anonymous-says-it-will-avenge-Charlie-Hebdo-attacks-by-shutting-down-jihadist-websites.html", 1189 | "text": "'Hacktivist' group Anonymous says it will avenge Charlie Hebdo attacks" 1190 | }, 1191 | "Karma": "4 points" 1192 | }, 1193 | { 1194 | "ID": "113", 1195 | "Title": { 1196 | "href": "http://blog.whttl.com/2015/01/09/ashton-speaks-to-ycombinator-startup-school/", 1197 | "text": "Ashton Kutcher's Speech to Y Combinator's Startup School" 1198 | }, 1199 | "Karma": "7 points" 1200 | }, 1201 | { 1202 | "ID": "114", 1203 | "Title": { 1204 | "href": "http://www.gamasutra.com/view/feature/131439/keeping_the_pirates_at_bay.php", 1205 | "text": "Keeping the Pirates at Bay – Copy and Crack Protection (2001)" 1206 | }, 1207 | "Karma": "49 points" 1208 | }, 1209 | { 1210 | "ID": "115", 1211 | "Title": { 1212 | "href": "https://docs.google.com/document/d/1OaatvGhEAq7VseQ9kkavxKNAfepWy2yhPUBs96FGV28/edit", 1213 | "text": "Go 1.5 Bootstrap Plan" 1214 | }, 1215 | "Karma": "133 points" 1216 | }, 1217 | { 1218 | "ID": "116", 1219 | "Title": { 1220 | "href": "https://www.codementor.io/learn-programming/now-good-time-learn-rust", 1221 | "text": "Is Now a Good Time to Learn Rust?" 1222 | }, 1223 | "Karma": "6 points" 1224 | }, 1225 | { 1226 | "ID": "117", 1227 | "Title": { 1228 | "href": "http://www.schneems.com/2015/01/09/how-to-troll-github-comments.html", 1229 | "text": "How to (not) Troll GitHub Comments" 1230 | }, 1231 | "Karma": "5 points" 1232 | }, 1233 | { 1234 | "ID": "118", 1235 | "Title": { 1236 | "href": "http://translations.unbabel.com/mailchimp/", 1237 | "text": "Show HN: Reach new markets by translating your MailChimp campaigns" 1238 | }, 1239 | "Karma": "4 points" 1240 | }, 1241 | { 1242 | "ID": "119", 1243 | "Title": { 1244 | "href": "http://www.thebaffler.com/salvos/the-problem-with-music", 1245 | "text": "The Problem with Music (1993)" 1246 | }, 1247 | "Karma": "65 points" 1248 | }, 1249 | { 1250 | "ID": "120", 1251 | "Title": { 1252 | "href": "https://medium.com/@richardeng/the-smalltalk-revolution-ee245c281f51", 1253 | "text": "The Smalltalk Revolution" 1254 | }, 1255 | "Karma": "80 points" 1256 | } 1257 | ] 1258 | } 1259 | ] 1260 | }; 1261 | 1262 | 1263 | //Util.deletePropByString(results, 'results.News'); 1264 | //console.log(Util.getPropByString(results, '')); 1265 | 1266 | new KimFilter(data) 1267 | .setCurrCollection('News') 1268 | .split({ 1269 | property: 'Karma', 1270 | separator: ' ', 1271 | names: ['num', 'unit'] 1272 | }) 1273 | .remove({ 1274 | property: 'unit', 1275 | operator: '!==', 1276 | target: undefined 1277 | }) 1278 | .toInt({ 1279 | property: 'num' 1280 | }) 1281 | .currencyConvert({ 1282 | property: 'num', 1283 | from: 'USD', 1284 | to: 'CAD', 1285 | decimal: 3 1286 | }) 1287 | .renameProperty({ 1288 | properties: ['num', 'unit'], 1289 | newnames: ['Num', 'Unit'] 1290 | }) 1291 | //.remove({ 1292 | // property: 'num', 1293 | // operator: '>', 1294 | // target: 100 1295 | //}) 1296 | //.sort({ 1297 | // property: 'num', 1298 | // lowToHigh: 0 1299 | //}) 1300 | //.toFloat({ 1301 | // property: 'key1', 1302 | // decimal: 2 1303 | //}) 1304 | //.merge({ 1305 | // properties: ['key1', 'key2'], 1306 | // newProperties: ['num', 'unit'], 1307 | // newProp: 'Karma' 1308 | //}) 1309 | //.renameProperty({ 1310 | // property: 'Karma', 1311 | // newname: 'KM' 1312 | //}) 1313 | //.removeProp({ 1314 | // properties: ['Title'] 1315 | //}) 1316 | //.sort({ 1317 | // property: 'KM.num', 1318 | // lowToHigh: 1 1319 | //}) 1320 | //.replace({ 1321 | // property: 'KM.unit', 1322 | // from: /^pts$/, 1323 | // to: 'pTs' 1324 | //}) 1325 | //.toInt({ 1326 | // property: 'KM.num', 1327 | //}) 1328 | //.merge({ 1329 | // properties: ['KM.num', 'KM.unit'], 1330 | // newProp: 'New_KM', 1331 | // newProperties: ['km_num', 'km_unit'] 1332 | //}) 1333 | //.removeProp({ 1334 | // properties: ['KM'], 1335 | //}) 1336 | //.sort({ 1337 | // property: 'New_KM.km_num' 1338 | //}) 1339 | .output(function(err, data) { 1340 | if(err) { 1341 | console.log("heeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"); 1342 | console.log("ERR: ", err); 1343 | } else { 1344 | data.results['News'].forEach(function(val, idx, arr) { 1345 | console.log(val); 1346 | }); 1347 | //data.results.forEach(function(entry, idx, arr) { 1348 | // console.log(entry); 1349 | //}); 1350 | } 1351 | }); 1352 | 1353 | 1354 | //request('http://finance.yahoo.com/d/quotes.csv?e=.csv&f=sl1d1t1&s=' + 'USD'+ 'INR' + '=X', function(err, res, body) { 1355 | // // "USDINR=X",63.1559,"1/7/2015","5:39pm" 1356 | // var val = body.split(',')[1]; 1357 | // console.log(val) 1358 | //}); 1359 | --------------------------------------------------------------------------------