0

I do not know if it's possible but I would factorize functions like

  // wiki
  if(command === "wiki"){
    bangSearch('wikiSearch','_',args);
  }

  // afr amazon fr
  if(command === ("afr")){
    bangSearch('amazonSearch','+',args);
  }

  function wikiSearch(recherche){
    var url = "https://fr.wikipedia.org/w/api.php?action=opensearch&search="+recherche+"&limit=1&namespace=0&format=json";
    request(url, function(err, resopnse, json){
      //some code
    });
  }

  function amazonSearch(recherche){
    var url = "https://www.amazon.fr/s/ref=nb_sb_noss?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&url=search-alias%3Daps&field-keywords="+recherche;
    message.channel.send('Recherche amazon pour: '+recherche+'\n'+url);
  }

  function bangSearch(searchFunctionName,keywordSeparator,args){

    if(args.length > 1){
      searchFunctionName(args.join(keywordSeparator));
    }else if (args.length == 0) {
      searchFunctionName(/*some args*/);
    }else{
      searchFunctionName(args[0]);
    }
  }

But when i run the code, I have this error: TypeError: searchFunctionName is not a function

So, I understand that the code does not call searchFunctionName like wikiSearch or amazonSearch but it only understands that the called function is searchFunctionName.

So I can not call the argument function which is either "wikiSearch" or "amazonSearch"

Is it possible to do this? Can you help me ?

The entire code: Here is the entire code. It's for a discord bot and i use Discord.js

 // wiki
  if(command === "wiki"){
    console.log(args);
    bangSearch(wikiSearch,'_',args);
  }

  // afr amazon fr
  if(command === ("afr")){
    bangSearch(amazonSearch,'+',args);
  }


  function wikiSearch(recherche){
    var url = "https://fr.wikipedia.org/w/api.php?action=opensearch&search="+recherche+"&limit=1&namespace=0&format=json";
    request(url, function(err, resopnse, json){
      try {
        var name = JSON.parse(json)[1];
        var link = JSON.parse(json)[3];
        if(name ==='undefined'){
          message.channel.send('Aucun résultats');
        }else {
          message.channel.send('Recherche wikipedia pour: '+recherche);
          message.channel.send('Nom: '+name[0]+'\n'+link[0]+'\n\n');
        }
      } catch (e) {
        callback('ERREUR: '+e);
      }
    });
  }

  function amazonSearch(recherche){
    var url = "https://www.amazon.fr/s/ref=nb_sb_noss?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&url=search-alias%3Daps&field-keywords="+recherche;
    message.channel.send('Recherche amazon pour: '+recherche+'\n'+url);
  }

  function bangSearch(searchFunctionName,keywordSeparator,args){

    if(args.length > 1){
      searchFunctionName(args.join(keywordSeparator));
    }else if (args.length == 0) {
      message.channel.send('tu veux quoi ?').then(() => {
        message.channel.awaitMessages(response => response.content.length > 0 , {
          max: 1,
          time: 10000,
          errors: ['time'],
        }).then((collected) => {
          searchFunctionName(collected.first().content);
        }).catch(() => {
          message.channel.send('T\'as pas trouvé les touches sur ton clavier ou quoi ?');
        });
      });
    }else{
      searchFunctionName(args[0]);
    }
  }
BarbeBleue
  • 45
  • 6
  • 1
    https://stackoverflow.com/questions/5905492/dynamic-function-name-in-javascript - It's not really a nice way to do this. Instead use an object. – evolutionxbox Nov 22 '17 at 16:42
  • just pass the function name, not a string. – Davin Tryon Nov 22 '17 at 16:42
  • 1
    @DavinTryon that would circumvent using eval. phew – evolutionxbox Nov 22 '17 at 16:43
  • @DavinTryon, I assume you mean a reference to the function. The function name IS a string, and it is exactly what he is passing right now. – Thomas Nov 22 '17 at 16:48
  • 1
    @espacarello yes, but no! `bangSearch(amazonSearch)` would be much better – Jonas Wilms Nov 22 '17 at 16:49
  • How about an explicit mapping: `const searchFunctions = {afr: amazonSearch, wiki: wikiSearch}; searchFunctions["afr"]("bla");` – le_m Nov 22 '17 at 16:49
  • @espacarello I don't think that is the correct duplicate. In this case, the OP has the function, not just its name as a string. better to pass the function (by reference) directly. – Davin Tryon Nov 22 '17 at 16:52
  • @davin thats why i voted to reopen ( i love democracy ;)), so would you mind adding that as an answer? – Jonas Wilms Nov 22 '17 at 16:53
  • 1
    @BarbeBleue, what are you trying to build here? What is the purpose of `bangSearch()`? At the moment, `bangSearch('wikiSearch','_',args)` is just a complicated way to do `wikiSearch(args.join("_"))`. So why would you want to use it? How do you think you benefit from this construct? – Thomas Nov 22 '17 at 16:55
  • @Thomas, because i would like to use the same code in bangSearch, when i use wikiSearch OR amazonSearch – BarbeBleue Nov 22 '17 at 16:57
  • 1
    @DavinTryon I must have made a mistake somewhere but actually passed the name of the function without the quotes, works thank you ! – BarbeBleue Nov 22 '17 at 16:57
  • @barbebleue by the way `searchFunctionName(args.join(seperator))` works in every case ( 0 or 1 element)... – Jonas Wilms Nov 22 '17 at 16:59
  • 1
    @BarbeBleue I don't understand, all the code in `bangSearch` is merely a Array#join and the conditions are pointless. you can reduce it to `function bangSearch(searchFunction,keywordSeparator,args){ searchFunction(args.join(keywordSeparator)); }` and call `bangSearch(wikiSearch, '_', args)` or you simply call `wikiSearch(args.join('_'))`. To me, bangSearch just complicates the code without real benefit. – Thomas Nov 22 '17 at 17:06

3 Answers3

1

Write a function keywordSearch to compose a function that can correctly handle keywords:

// aka bangSearch
const keywordSearch = (searchFn) => 
  (...keywords) => keywords.length ? 
    searchFn(...keywords) :
      // your complicated logic...;

Use the keywordSearch function to compose your keyword search functions:

const wikipediaUrl = (keywords) => `https://fr.wikipedia.org/w/api.php?action=opensearch&search=${keywords.join('_')}&limit=1&namespace=0&format=json`;
const wikipedia = keywordSearch((...keywords) => 
  new Promise((resolve, reject) =>         
    request(wikipediaUrl(keywords), (err, response) => 
      err ? reject(err) : resolve(response));

const amazonUrl = (keywords) => `https://www.amazon.fr/s/ref=nb_sb_noss?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&url=search-alias%3Daps&field-keywords=${keywords.join('+')}`;
const amazon = keywordSearch((...keywords) => 
  message.channel.send(`Recherche amazon pour:${keywords.join(',')} \n ${amazonUrl(keywords)}`);

Expose the keyword search functions on an object:

const providers = { wikipedia, amazon, };

Or if you are using ES6 modules you can use the module syntax to be clever:

// search-providers.js
export * as wikpedia from './wikipedia';
export * amazon from './amazon';

Then use the search provider.

['wikipedia'] uses the indexer syntax for retrieving the value of an object property:

import * as providers from './search-providers'; // if you are using modules
const runTheSearch = async () => {
  // ...
  const result = await providers['wikipedia']('this is my search'); // assuming everything is promisified 
}
Ben Aston
  • 53,718
  • 65
  • 205
  • 331
1

Since JavaScript is functional, you can pass functions (by reference) to another function! So, with the least amount of changes, this should work for you:

// wiki
  if(command === "wiki"){
    bangSearch(wikiSearch,'_',args);
  }

  // afr amazon fr
  if(command === ("afr"){
    bangSearch(amazonSearch,'+',args);
  }

  function wikiSearch(recherche){
    var url = "https://fr.wikipedia.org/w/api.php?action=opensearch&search="+recherche+"&limit=1&namespace=0&format=json";
    request(url, function(err, resopnse, json){
      //some code
    });
  }

  function amazonSearch(recherche){
    var url = "https://www.amazon.fr/s/ref=nb_sb_noss?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&url=search-alias%3Daps&field-keywords="+recherche;
    message.channel.send('Recherche amazon pour: '+recherche+'\n'+url);
  }

  function bangSearch(searchFunctionName,keywordSeparator,args){

    if(args.length > 1){
      searchFunctionName(args.join(keywordSeparator));
    }else if (args.length == 0) {
      searchFunctionName(/*some args*/);
    }else{
      searchFunctionName(args[0]);
    }
  }

This takes advantage of function hoisting so the amazonSearch and wikiSearch functions do not have to be defined above where their references are used. However, for readability, you might want to define them above the usage.

Davin Tryon
  • 66,517
  • 15
  • 143
  • 132
0

You can create a map of your search functions which then you can access using the keys. Yet another solution. Also it will be nicer if you pass in the searchFunctions also as an argument rather than keeping it as a side-effect.

If there is only one item in array, join will in effect return the first item as a string so, you only need two conditions there.

['something'].join('+'); // "something"

// wiki
var command = "wiki";
var args = [1, 2, 3];

var searchFunctions = {
  wikiSearch: function(recherche) {
    console.log(recherche)
  },
  amazonSearch: function(recherche) {
    console.log(recherche)
    }
}

switch (command) {
  case 'wiki':
    bangSearch('wikiSearch', '_', args);
    break;
  case 'amazon':
    bangSearch('amazonSearch', '+', args);
    break;
}

function bangSearch(searchFunctionName, keywordSeparator, args) {
  if (args.length) {
    searchFunctions[searchFunctionName](args.join(keywordSeparator));
  } else {
    searchFunctions[searchFunctionName]( /*some args*/ );
  }
}
sabithpocker
  • 15,274
  • 1
  • 42
  • 75