Как создать динамический фильтр на основе динамических параметров? - PullRequest
0 голосов
/ 06 апреля 2019

Мне трудно найти решение для генерирования необходимой логики и вывода на основе динамической фильтрации. Я работаю над сценарием в Google Apps Script и использую doGet(), чтобы действовать как веб-крючок. Параметры отправляются в этот веб-крючок, а затем на основе предоставленных параметров данные фильтруются из электронной таблицы. Это означает, что я должен создать свой собственный фильтр на основе отправленных параметров. Изначально я начал пытаться создать условия вручную. Однако я понял, что комбинация необязательных параметров превышает 250, и это заняло бы у меня целую вечность! Поток идет так:

Это все параметры, которые принимаются: (exch,base,dir,durmin,durmax,drawmin,drawmax,askmin,askmax,excessmin,excessmax,maxdcalvl,maxdcapercent,numcalls)

Из них есть 6 обязательных параметров: (exch,base,dir,durmax,drawmax,maxdcalvl)

Это необязательно: (durmin,drawmin,askmin,askmax,excessmin,excessmax,maxdcapercent,numcalls)

Логика, необходимая для каждого параметра, соответствует следующей схеме: exch == exchange && base == baseC && dir == direction

duration >= durmin && duration <= durmax drawdown <= drawmin && drawdown >= drawmax

Здесь важно отметить, что эти параметры в основном просто помогают найти данные на листе exch == exchange && base == baseC && dir == direction и, по сути, просто возвращаются. Других сравнений с ними нет.

Другие параметры следуют этой схеме: duration >= durmin && duration <= durmax drawdown <= drawmin && drawdown >= drawmax Где первая переменная - это данные из цикла for, которые необходимо отфильтровать, а переменные min / max - это минимальные и максимальные параметры, отправляемые пользователем, которые используются для фактической фильтрации данных, например askSpread (for loop data) >= askmin (user input) && askSpread (for loop data) <= askmax (user input). Опять же, каждый из этих параметров является необязательным, поэтому пользователь может в итоге отправить только askmin, и, таким образом, мне потребуется только askSpread >= askmin вместо askSpread (for loop data) >= askmin (user input) && askSpread (for loop data) <= askmax (user input) или наоборот с максимальным значением

.

Итак, упрощенное объяснение - если параметр, требуемый или необязательный, не имеет обоих min / max, тогда я просто проверяю, что вызываемые данные поступают из правильного места на листе AKA: exch == binance && base == btc && dir == long

Все, что с мин / макс. Я использую их для создания критериев мин и макс. duration >= durmin && duration <= durmax - Отфильтруйте данные, где данные> = durmin (скажем, 1), а данные <= durmax (скажем, 4) </p>

Это фрагмент моего кода, который возвращает правильные значения. Однако этот метод требует от меня ввода всех 250+ комбинаций параметров, основанных на выражениях if. Я думаю .filter() это лучшее решение. Но я не знаю, как правильно его использовать. Любая помощь будет принята с благодарностью, так как на данный момент я полностью ошеломлен PS. Если вы не знакомы с Google Apps Script, я ограничен в некоторых функциях Javascript.

Apps Script - это язык сценариев для разработки легких приложений> на платформе G Suite. Он основан на JavaScript 1.6 с некоторыми частями> 1,7 и 1,8 и предоставляет подмножество API-интерфейса ECMAScript 5 [2], однако вместо> запуска на клиенте он выполняется в облаке Google. Согласно Google, «Сценарий приложений» «предоставляет простые способы автоматизации задач для продуктов> Google и сторонних сервисов». [3] «Сценарий приложений» - это также инструмент, который> расширяет возможности надстроек для Документов, листов и слайдов Google. [4 ]

https://en.wikipedia.org/wiki/Google_Apps_Script

function test() {
  //  exch,base,dir,durmin,durmax,drawmin,drawmax,askmin,askmax,excessmin,excessmax
  var d = data('binance','bnb','long',0.5,3,-1,-3,0,0,5,0);
  Logger.log(d)

}

function data(exch,base,dir,durmin,durmax,drawmin,drawmax,askmin,askmax,excessmin,excessmax){
  var exch = exch.toLowerCase();
  var base = base.toUpperCase();
  var dir = dir.toUpperCase();

  var s = SpreadsheetApp;
  var ss = s.getActiveSpreadsheet();
  var saved = ss.getSheetByName('Signals');
  var lastRow = saved.getLastRow();
  var data = saved.getRange(2, 1, lastRow, 12).getValues();

  if (drawmin > 0) {    
   var drawmin = drawmin/100
  }
  if (drawmax > 0) {  
   var drawmax = drawmax/100
  }
  if (askmin > 0) {
   var askmin = askmin/100
  }
  if (askmax > 0) {
   var askmax = askmax/100
  }
  if (excessmin > 0) {
   var excessmin = excessmin/100
  }
  if (excessmax > 0) {
    var excessmax = excessmax/100
  }

  var array = []
  for (var i = 0; i < data.length; i++) {

    var exchange = data[i][0];
    var baseC = data[i][1];
    var direction = data[i][2];
    var coin = data[i][3];
    var duration = data[i][4];
    var drawdown = data[i][5];
    var askSpread = data[i][6];
    var excess = data[i][7];

    if(exch == exchange && base == baseC && dir == direction && duration >= durmin && duration <= durmax && drawdown <= drawmin && drawdown >= drawmax && askSpread >= askmin && askSpread <= askmax && excess >= excessmin && excess <= excessmax) {
      Logger.log('1')  
      array.push(
          {
            "exchange": exchange,
            "base": baseC,
            "direction": direction,
            "coin": coin,
            "duration": Number(duration.toPrecision(2)),
            "draw_down": Number((drawdown*100).toPrecision(2)),
            "ask_highest_spread": Number((askSpread*100).toPrecision(2)),
            "excess_above_target": Number((excess*100).toPrecision(2))

          }
        )
    } else if(exch == exchange && base == baseC && dir == direction && duration >= durmin && duration <= durmax && drawdown <= drawmin && drawdown >= drawmax && askSpread >= askmin && askSpread <= askmax && excess >= excessmin && excess <= excessmax) {
      Logger.log('2')   
      array.push(
          {
            "exchange": exchange,
            "base": baseC,
            "direction": direction,
            "coin": coin,
            "duration": Number(duration.toPrecision(2)),
            "draw_down": Number((drawdown*100).toPrecision(2)),
            "ask_highest_spread": Number((askSpread*100).toPrecision(2)),
            "excess_above_target": Number((excess*100).toPrecision(2))
          }
        )
    } else if(exch == exchange && base == baseC && dir == direction && duration >= durmin && duration <= durmax && drawdown <= drawmin && drawdown >= drawmax && askSpread >= askmin && askSpread <= askmax && excess >= excessmin) {
      Logger.log('3')  
      array.push(
          {
            "exchange": exchange,
            "base": baseC,
            "direction": direction,
            "coin": coin,
            "duration": Number(duration.toPrecision(2)),
            "draw_down": Number((drawdown*100).toPrecision(2)),
            "ask_highest_spread": Number((askSpread*100).toPrecision(2)),
            "excess_above_target": Number((excess*100).toPrecision(2))
          }
        )
    } else if(exch == exchange && base == baseC && dir == direction && duration >= durmin && duration <= durmax && drawdown <= drawmin && drawdown >= drawmax && askSpread >= askmin && askSpread <= askmax) {
      Logger.log('4')  
      array.push(
          {
            "exchange": exchange,
            "base": baseC,
            "direction": direction,
            "coin": coin,
            "duration": Number(duration.toPrecision(2)),
            "draw_down": Number((drawdown*100).toPrecision(2)),
            "ask_highest_spread": Number((askSpread*100).toPrecision(2)),
            "excess_above_target": Number((excess*100).toPrecision(2))
          }
        )
    } else if(exch == exchange && base == baseC && dir == direction && duration >= durmin && duration <= durmax && drawdown <= drawmin && drawdown >= drawmax && askSpread >= askmin) {
      Logger.log('5')   
      array.push(
          {
            "exchange": exchange,
            "base": baseC,
            "direction": direction,
            "coin": coin,
            "duration": Number(duration.toPrecision(2)),
            "draw_down": Number((drawdown*100).toPrecision(2)),
            "ask_highest_spread": Number((askSpread*100).toPrecision(2)),
            "excess_above_target": Number((excess*100).toPrecision(2))
          }
        )
    } else if(exch == exchange && base == baseC && dir == direction && duration >= durmin && duration <= durmax && drawdown >= drawmax && askSpread >= askmin) {
      Logger.log('6')    
      array.push(
          {
            "exchange": exchange,
            "base": baseC,
            "direction": direction,
            "coin": coin,
            "duration": Number(duration.toPrecision(2)),
            "draw_down": Number((drawdown*100).toPrecision(2)),
            "ask_highest_spread": Number((askSpread*100).toPrecision(2)),
            "excess_above_target": Number((excess*100).toPrecision(2))
          }
        )
    } else if (exch == exchange && base == baseC && dir == direction && duration <= durmax && drawdown >= drawmax && askSpread >= askmin) {
      Logger.log('7')  
      array.push(
          {
            "exchange": exchange,
            "base": baseC,
            "direction": direction,
            "coin": coin,
            "duration": Number(duration.toPrecision(2)),
            "draw_down": Number((drawdown*100).toPrecision(2)),
            "ask_highest_spread": Number((askSpread*100).toPrecision(2)),
            "excess_above_target": Number((excess*100).toPrecision(2))
          }
        )

    }
  }
  return array
}

Ответы [ 2 ]

2 голосов
/ 06 апреля 2019

При разборе строки запроса установите значения по умолчанию для необязательных параметров 1 , если отсутствует.

Фрагмент:

function doGet(f){
  const e = f.parameter;
  const durmin = e.durmin || -Infinity;
  const durmax = e.durmax || Infinity;
1 голос
/ 06 апреля 2019

При работе с двумерными массивами может быть полезно преобразовать нотацию индекса в аннотированный объект, сгладить объект и заменить доступ к индексу доступом к свойству:

const relationship = {
  // array index: property name
  0: "exchange",
  ...
};
var annotated = data.map(function (row) {
  // row is an array, lets make it an object:
  return row.reduce(function (obj, value, idx) {
    var prop = relationship[idx];
    obj[prop] = value;
    return obj;
  }, {});
});
// `annotated` is now an array of objects with usefully named properties instead of an array of arrays of values

Следующий шаг - начать фильтрацию. Похоже, у вас есть два вида тестов - равенство и «в диапазоне», и, вероятно, их легче всего обрабатывать отдельно. Предполагая, что вы создали объект с ключами, указанными пользователем (вместо использования множества отдельных переменных, мы используем один объект с этими переменными в качестве свойств), мы можем упростить проверки на равенство:

const requestedInfo = {
  "exchange": exchange,
  "base": base,
  "direction": direction,
  ...
};

// some request parameters denote "ranges," not equality; they don't go in here.
const requiredEqualKeys = ["exchange", "base", ... ];
var meetsEqualityCriteria = annotated.filter(function (obj) {
  return requiredEqualKeys.every(function (key) { return requestedInfo[key] === undefined || obj[key] === requestedInfo[key]; });
});

Метод Array#every вернет true (что говорит Array#filter сохранить этот объект), только если функция, вызванная для каждого значения в этом массиве (requiredEqualKeys), возвращает true. Наша проверка на равенство сначала определяет, был ли этот параметр задан в качестве входных данных, и только в том случае, если это было, запрашиваемое значение должно быть таким же, как и в фильтруемом объекте (например, если запрос не включает base, нет исключение на base будет сделано). Возможно, вам следует добавить проверку на наличие запрошенного свойства null; Я оставляю это читателю. (Если вы не аннотируете свой data, вы получите доступ к этим значениям только по индексу массива, и это действительно легко сделать неправильно, когда ваши данные изменяют формат / порядок!)

После того, как вы уменьшили объекты только до тех, которые удовлетворяют запрошенному равенству, вы можете применить дополнительные тесты, например, находится ли свойство в запрошенном диапазоне. Когда вы определили requestedInfo, для значений диапазона вы должны создать вложенный объект, например

const requestedInfo = {
  ...
  "ask": {
    "min": askMin, // can set defaults per @TheMaster's answer here too
    "max": askMax
  },
  ...
}

Это позволяет вам проверить свойство и определить, находится ли значение вашего объекта в диапазоне:

const rangeKeys = [ "ask", "duration", ... ];
var meetsEQandRangeCriteria = meetsEqualityCriteria.filter(function (obj) {
  return rangeKeys.every(function (prop) {
    var rqProp = requestedInfo[prop];
    if (rqProp === undefined)
      return true; // property not present in request
    if (!obj.hasOwnProperty(prop) || obj[prop] < rqProp.min || obj[prop] > rqProp.max)
      return false; // not present in object but was requested, or out-of-range 
    else
      return true;
  });
});

Теперь у вас есть массив объектов, которые удовлетворяют всем критериям равенства и диапазона, которые вы запрашивали. Вы можете вернуть его как есть или в соответствии с вашим кодом в сообщении вернуть отформатированный эквивалент:

return meetsEQandRangeCriteria.map(function (obj) {
  return {
    "base": obj.base,
    ...
    "excess_above_target": Number((obj.excess * 100).toPrecision(2))
  };
});

Ссылки:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...