创建一个生成器来迭代对象属性置换 [英] Create a generator to iterate object property permutations

查看:149
本文介绍了创建一个生成器来迭代对象属性置换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个 Range 类,它创建一个生成函数,用于遍历一整个整数。我的下一步是创建一个生成函数,迭代每个属性的值的所有可能的排列。以下是硬编码示例的简化代码:

  //创建参数定义(完美工作)。 
const paramDef = {
propA:new Range(1,2,3),// [1,2,3] as iterator
propB:new Range(2,4,6) // [2,4,6] as iterator
};

//硬编码实现(目标是使这个通用/可重用)
函数* getUnits(def){
// foreach value of propA ...
for(let valpropA of def.propA.getValues()){
// and foreach value of propB ...
for(let valPropB of def.propB.getValues()){
//产生一个具有当前值的实例...
yield {
propA:valPropA,
propB:valPropB
};
}
}
}

//逐个迭代,创建对象属性的排列。
(getUnits(paramDef)){
console.log(unit);
}

//输出:
// {propA:1,propB:2}
// {propA:1, propB:4}
// {propA:1,propB:6}
// {propA:2,propB:2}
// { propA:2,propB:4}
// {propA:2,propB:6}
// {propA:3,propB:2}
// {propA:3,propB:4}
// {propA:3,propB:6}
/ pre>

我已经尝试了很多东西,但是我获得的最远的是让第一次迭代正确返回,但没有其他的。你如何泛化 getUnits()函数以及我应该注意什么陷阱?

解决方案

这是主要功能(下面的完整代码):

  //通用实现
函数* getUnits(def,props,obj){
props = props || [];

//如果没有剩余的属性...
if(props.length === 0){
//然后我们可能会开始...
if(typeof obj ==='undefined'){
//从定义中获取属性名称。
props = Object.keys(def);
//并创建一个空对象
obj = {};
} else {
yield obj;
return;
}
}

//抓住第一支柱和其余道具的副本。
let currentProp = props [0];
let remainingProps = props.slice(1);
//当前工作的foreach值
为(let val of def [currentProp] .getValues()){

//将值分配给一个新的实例
let currentObj = Object.assign({},obj,{
[currentProp]:val
});
//将定义,remainingProps和新实例传递到下一个级别(较小的属性子集)
yield * getUnits(def,remainingProps,currentObj);
}
}

我发现的最好的资源是 MDN的示例与 yield * 演示。如果您希望更好地了解ES6发电机,请务必阅读整篇文章。



信用证出售给@Bergi,表示收益'ed对象实例在所有情况下都是一样的,当理想情况下应该在每个分支中克隆(因此它们各自是不同的实例)。



整个例子包含在这个片段中(运行它来查看结果)。



  // Helper类,提供了一个迭代器从一组args.class Range {constructor(){this.values = Array.prototype.slice.call(arguments); } * getValues(){for(let i = 0; i< this.values.length; i ++){yield this.values [i]; }}} //创建参数定义(完美的工作).const paramDef = {a:new Range(1,2,3),b:new Range(0,1),c:new Range(1,1,2 ,3,5)}; //通用实现函数* getUnits(def,props,obj){props = props || []; //如果没有剩余的属性... if(props.length === 0){//然后我们可能会开始... if(typeof obj ==='undefined'){//抓住属性来自定义的名字。 props = Object.keys(def); //并创建一个空对象obj = {}; } else {yield obj;返回; }} //抓住第一个支柱和剩下道具的副本。让currentProp =道具[0]; let remainingProps = props.slice(1); // foreach value of currentProp ... for(let val of def [currentProp] .getValues()){//将值分配给一个新的实例let currentObj = Object.assign({},obj,{[currentProp] :val}); //将定义,remainingProps和新实例传递到下一级(属性的较小子集)yield * getUnits(def,remainingProps,currentObj); }} let outputStr =''; //迭代一个,创建一个对象属性的排列(for getUnits(paramDef)){outputStr + = JSON.stringify(unit)+'\\\
' ;} alert(outputStr); //输出://查看控制台的结果...


I've created a Range class that creates a generator function for iterating over a range of integers. My next step is to create a generator function that iterates all possible permutations of values for each property. Here is the simplified code for a hard-coded example:

// Create the parameter definitions (works perfectly).
const paramDef = {
  propA: new Range(1, 2, 3), // [1,2,3] as iterator
  propB: new Range(2, 4, 6)  // [2,4,6] as iterator
};

// Hardcoded implementation (the goal is to make this generic/re-usable)
function* getUnits(def){
  // Foreach value of propA...
  for(let valPropA of def.propA.getValues()){
    // and foreach value of propB...
    for(let valPropB of def.propB.getValues()){
      // Yield an instance with the current values...
      yield {
        propA: valPropA,
        propB: valPropB
      };
    }
  }
}

// Iterate one-by-one, creating a permutation of object properties.
for(let unit of getUnits(paramDef)){
    console.log(unit);
}

// Outputs:
// {"propA":1,"propB":2}
// {"propA":1,"propB":4}
// {"propA":1,"propB":6}
// {"propA":2,"propB":2}
// {"propA":2,"propB":4}
// {"propA":2,"propB":6}
// {"propA":3,"propB":2}
// {"propA":3,"propB":4}
// {"propA":3,"propB":6}

I've tried a number of things, but the furthest I've gotten was to get the first iteration to return correctly, but nothing else. How do you generalize the getUnits() function and what traps should I look out for?

解决方案

This is the primary function (full code below):

// Generic implementation 
function* getUnits(def, props, obj){
    props = props || [];

  // If there are no remaining properties...
  if(props.length === 0){
    // Then we might be starting out...
    if(typeof obj === 'undefined'){
        // Grab the property names from the definition.
        props = Object.keys(def);
      // And create an empty object 
      obj = {};
    } else {
        yield obj;
      return;
    }
  }

  // Grab the first prop and a copy of the remaining props.
  let currentProp = props[0];
  let remainingProps = props.slice(1);
  // Foreach value of the currentProp...
  for(let val of def[currentProp].getValues()){

    // Assign the value to a new instance
    let currentObj = Object.assign({}, obj, {
        [currentProp]: val
    });
    // Pass the definition, remainingProps, and the new instance to the next level down (smaller subset of properties)
    yield* getUnits(def, remainingProps, currentObj);
  }
}

The best resource I found was MDN's Example with yield* demo. Definitly worth reading that whole article if you want a better understanding of ES6 Generators.

Credit goes out to @Bergi for indicating that the yield'ed object instance is the same in all cases, when ideally it should be cloned at each branch (so they are each different instances).

The entire example is contained in this snippet (run it to see the outcome).

// Helper class, provides an iterator from a set of args.
class Range {
  constructor() {
    this.values = Array.prototype.slice.call(arguments);
  }

  * getValues() {
    for (let i = 0; i < this.values.length; i++) {
      yield this.values[i];
    }
  }
}

// Create the parameter definitions (works perfectly).
const paramDef = {
  a: new Range(1, 2, 3),
  b: new Range(0, 1),
  c: new Range(1, 1, 2, 3, 5)
};

// Generic implementation 
function* getUnits(def, props, obj){
	props = props || [];
  
  // If there are no remaining properties...
  if(props.length === 0){
  	// Then we might be starting out...
  	if(typeof obj === 'undefined'){
    	// Grab the property names from the definition.
    	props = Object.keys(def);
      // And create an empty object 
      obj = {};
    } else {
    	yield obj;
      return;
    }
  }
  
  // Grab the first prop and a copy of the remaining props.
  let currentProp = props[0];
  let remainingProps = props.slice(1);
  // Foreach value of the currentProp...
  for(let val of def[currentProp].getValues()){
  	
    // Assign the value to a new instance
    let currentObj = Object.assign({}, obj, {
    	[currentProp]: val
    });
    // Pass the definition, remainingProps, and the new instance to the next level down (smaller subset of properties)
    yield* getUnits(def, remainingProps, currentObj);
  }
}

let outputStr = '';

// Iterate one-by-one, creating a permutation of object properties.
for (let unit of getUnits(paramDef)) {
  outputStr += JSON.stringify(unit) + '\n';
}
alert(outputStr);

// Outputs:
// See console for the result...

这篇关于创建一个生成器来迭代对象属性置换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆