JS动态生成循环

时间:2018-04-21 03:20:45

标签: javascript

我正在使用第三方API,允许我搜索住房属性。遗憾的是,API不是以允许我搜索范围的方式编写的,因此我必须为范围内的每个值单独调用。

因此,如果我想搜索所有有2或3间卧室的房屋,我必须打电话给2间卧室,然后另外打3间卧室。现在这可能变得非常棘手,因为有多个字段可以包含一系列数字(卧室,浴室,地板,车库大小......)。

我的强力JavaScript解决方案是创建一个嵌套的for循环,它将创建一个包含所有调用的数组。这不是一个可扩展的解决方案,我正在寻找一种动态创建for循环的方法或另一种获取所有调用数组的替代方法。

我目前的解决方案:

const searchParams = {
    minBedrooms: 2,
    maxBedrooms: 4,
    minBathrooms: 1,
    maxBathrooms: 3,
    minFloors: 1,
    maxFloors: 1
};

let promises = [];

for (let bedrooms = searchParams.minBedrooms; bedrooms <= searchParams.maxBedrooms; bedrooms++) {
    for (let bathrooms = searchParams.minBathrooms; bathrooms <= searchParams.maxBathrooms; bathrooms++) {
        for (let floors = searchParams.minFloors; floors <= searchParams.maxFloors; floors++) {
            promises.push(callApi(bedrooms, bathrooms, floors));
        }
    }
}

Promise.all(promises).then(response => {
    // do stuff with response
}

此外,用户可能未指定其中一个搜索参数(即 - 卧室数)。因此,API不会应用该特定过滤器。我的代码当前将失败,没有传入卧室值,并且为每个for循环写入条件语句不是我的愿望。

关于如何动态生成上述嵌套for循环的任何想法?

修改

如果用户未指定卧室数量但指定浴室/楼层,我的当前解决方案将失败,因为循环的初始化将无法运行。我不想使用条件语句和许多嵌套循环来创建我的promise数组。这就是为什么我觉得我需要使用动态生成的for循环。

5 个答案:

答案 0 :(得分:6)

查看此内容的一种方法称为笛卡尔积[{1}} - 适用于A × B × C中的aA中的b以及{{1}在B中,您需要一个元组c

例如C有4个结果元组:(a, b, c){1, 2} × {3, 4}(1, 3)(1, 4)

最简单的方法是从第一组中的选项开始:(2, 3)(2, 4)。然后,对于第二组中的每个选项,使用新值完成每个元组:

  • (1),添加了(2)的{​​{1}}获取(1)(2)
  • 3,添加了(1, 3)的{​​{1}}获取(2, 3)(1)

在代码中,这可能如下所示:

(2)

4结尾,如

(1, 4)

答案 1 :(得分:1)

与Curtis F的答案类似,您可以使用类似

的内容
    self.global_settings_frame = wx.Frame(parent=self, title="Global Settings", name="Global Settings")
    self.global_settings_listbook = wx.Listbook(parent=self.global_settings_frame, style=wx.LB_LEFT)
    self.global_settings_file_window = wx.Panel(parent=self.global_settings_listbook)
    self.global_settings_file_box = wx.BoxSizer(orient=wx.VERTICAL)
    self.show_full_pathname_checkbox = wx.CheckBox(self.global_settings_file_window, label="Show full pathname")
    self.global_settings_file_box.Add(self.show_full_pathname_checkbox, proportion=1)
    self.global_default_extension = wx.TextCtrl(self.global_settings_file_window)
    self.global_settings_file_box.Add(self.global_default_extension, proportion=1)

    self.global_settings_token_window = wx.Panel(parent=self.global_settings_listbook)

    self.global_settings_listbook.InsertPage(0, self.global_settings_file_window, "Files")
    self.global_settings_listbook.InsertPage(1, self.global_settings_token_window, "Token Defnition")
    self.global_settings_frame.Show()

然后以类似的方式构建“元组”,除了for self.global_settings_frame = wx.Frame(parent=self, title="Global Settings", name="Global Settings") self.global_settings_listbook = wx.Listbook(parent=self.global_settings_frame, style=wx.LB_LEFT) self.global_settings_file_window = wx.Panel(parent=self.global_settings_listbook) self.global_settings_file_box = wx.BoxSizer(orient=wx.VERTICAL) self.show_full_pathname_checkbox = wx.CheckBox(self.global_settings_file_window, label="Show full pathname") self.global_settings_file_box.Add(self.show_full_pathname_checkbox, proportion=1) self.global_default_extension = wx.TextCtrl(self.global_settings_file_window) self.global_settings_file_box.Add(self.global_default_extension, proportion=1) self.global_settings_token_window = wx.Panel(parent=self.global_settings_listbook) self.global_settings_listbook.InsertPage(0, self.global_settings_file_window, "Files") self.global_settings_listbook.InsertPage(1, self.global_settings_token_window, "Token Defnition") self.global_settings_frame.Show() 中每个对象的for循环需要从const properties = { "bedrooms": {min: 2, max: 3}, "bathrooms": {min: 1, max: 2}, "floors": {min: 1, max: 4}, } 计算到min

答案 2 :(得分:0)

使用递归调用。

请尝试以下代码:

{
    function Looping ( parameters, fn, args = [] ) {
        if ( parameters.length ) {
            let [ [ key, param ], ...pass ] = parameters;
            let promises = [];
            for ( let i = param.min; i <= param.max; i++ ) {
                promises.push( ...Looping( pass, fn, [ ...args, i ] ) );
            }
            return promises;
        }
        else {
            return [ fn( ...args ) ];
        }
    }

    const searchParams = {
        Bedrooms: { min: 2, max: 4 },
        Bathrooms: { min: 1, max: 3 },
        Floors: { min: 1, max: 1 }
    };

    function callApi ( a, b, c ) { return Promise.resolve( `Bed: ${a}, Bath: ${b}, Floor: ${c}` ); }

    console.time( 'Recursive' );
    Promise.all( Looping( Object.entries( searchParams ), ( bedrooms, bathrooms, floors ) => callApi( bedrooms, bathrooms, floors ) ) )
    .then( a => {
        console.timeEnd( 'Recursive' );
        console.log( a );
    } );
}

递归调用类型比映射更快。

( async () => {
    await new Promise( resolve => {
        console.time( 'map' );
        function mm ( a, b ) { let r = []; for ( let i = a; i <= b; i++ ) r.push( i ); return r; }
        const properties = {
            Bedrooms: mm( 1, 100 ),
            Bathrooms: mm( 1, 100 ),
            Floors: mm( 1, 100 )
        };
        
        // Start with a single "empty" tuple
        let tuples = [{}]
        for (let p in properties) {
            // For each property, augment all of the old tuples
            let nextTuples = []
            for (let option of properties[p]) {
                // with each possible option
                nextTuples = nextTuples.concat(tuples.map(old => ({[p]: option, ...old})))
            }
            tuples = nextTuples;
        }
        let promises = [];
        function callApi ( a, b, c ) { return Promise.resolve( `Bed: ${a}, Bath: ${b}, Floor: ${c}` ); }
        for ( const i of tuples ) {
            let arg = [];
            for ( const [ k, v ] of Object.entries( i ) ) {
                arg.push( v );
            }
            promises.push( callApi( ...arg ) );
        }
        Promise.all( promises ).then( a => {
            console.timeEnd( 'map' );
            //console.log( a );
            resolve();
        } );
    } );

    await new Promise( resolve => {
        function Looping ( parameters, fn, args = [] ) {
            if ( parameters.length ) {
                let [ [ key, param ], ...pass ] = parameters;
                let promises = [];
                for ( let i = param.min; i <= param.max; i++ ) {
                    promises.push( ...Looping( pass, fn, [ ...args, i ] ) );
                }
                return promises;
            }
            else {
                return [ fn( ...args ) ];
            }
        }
    
        const searchParams = {
            Bedrooms: { min: 1, max: 100 },
            Bathrooms: { min: 1, max: 100 },
            Floors: { min: 1, max: 100 }
        };
    
        function callApi ( a, b, c ) { return Promise.resolve( `Bed: ${a}, Bath: ${b}, Floor: ${c}` ); }
    
        console.time( 'Recursive' );
        Promise.all( Looping( Object.entries( searchParams ), ( bedrooms, bathrooms, floors ) => callApi( bedrooms, bathrooms, floors ) ) )
        .then( a => {
            console.timeEnd( 'Recursive' );
            //console.log( a );
            resolve();
        } );
    } );
} )();

答案 3 :(得分:0)

您可以使用的一种方法是递归函数,其中每个图层将迭代矩阵的一个维度:

&#13;
&#13;
const dims = [
  { min: 2, max: 4 },
  { min: 1, max: 3 },
  { min: 1, max: 1 },
];

function matrix(dims, ...args) {
  const dim = dims[0];
  return dims.length
    ? [...new Array(dim.max - dim.min + 1)]
      .map((_,i) => i + dim.min)
      .map(x => matrix(dims.slice(1), ...args, x))
      .reduce((a, b) => [...a, ...b], [])
    : [callApi(...args)];
}

function callApi(bedrooms, bathrooms, floors) {
  return `bedrooms: ${bedrooms}, bathrooms: ${bathrooms}, floors: ${floors}`;
}

console.log(matrix(dims));
&#13;
&#13;
&#13;

答案 4 :(得分:0)

这不是一个答案,只需比较PO和最佳答案之间的表现。

我认为OP的代码足够好,只需要确保所有最大/最小值都有默认值。

我假设一个建筑有180层,最多5间卧室,最多5间浴室。

然后发现简单的三个嵌套循环(OP&#39; s解决方案)要快得多。

&#13;
&#13;
const searchParams = {
    minBedrooms: 1,
    maxBedrooms: 5,
    minBathrooms: 0,
    maxBathrooms: 5,
    minFloors: 1,
    maxFloors: 180
};
console.time("OP's")
let promises = [];

for (let bedrooms = searchParams.minBedrooms; bedrooms <= searchParams.maxBedrooms; bedrooms++) {
    for (let bathrooms = searchParams.minBathrooms; bathrooms <= searchParams.maxBathrooms; bathrooms++) {
        for (let floors = searchParams.minFloors; floors <= searchParams.maxFloors; floors++) {
            promises.push([bedrooms, bathrooms, floors]);
        }
    }
}
console.timeEnd("OP's")

const properties = {
    "bedrooms": [1, 2, 3, 4, 5],
    "bathrooms": [0, 1, 2, 3, 4, 5],
    "floors": Array.from(new Array(180).keys()),
}

console.time('Best Answer')
// Start with a single "empty" tuple
let tuples = [{}]

for (let p in properties) {
    // For each property, augment all of the old tuples
    let nextTuples = []
    for (let option of properties[p]) {
        // with each possible option
        nextTuples = nextTuples.concat(tuples.map(old => ({[p]: option, ...old})))
    }
    tuples = nextTuples;
}
console.timeEnd('Best Answer')
&#13;
&#13;
&#13;