创建一个生成器来迭代对象 属性 排列
Create a generator to iterate object property permutations
我创建了一个 Range
class,它创建了一个生成器函数,用于迭代一个整数范围。我的下一步是创建一个生成器函数,为每个 属性 迭代所有可能的值排列。这是硬编码示例的简化代码:
// 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}
我已经尝试了很多事情,但我得到的最深入的是将第一次迭代正确地 return,但没有别的。您如何概括 getUnits()
函数以及我应该注意哪些陷阱?
您可以对 属性 个名称的列表使用递归:
function getObjectsOf(def) {
var keys = Object.keys(def),
o = {};
return rec(keys.length);
function* rec(i) {
if (i <= 0) {
let clone = {}; // I assume you want to yield different objects
for (let k of keys) // or: k in o
clone[k] = o[k];
yield clone;
} else {
let key = keys[i];
for (let value of def[key]) {
o[key] = value;
yield* rec(i-1);
}
}
}
}
如果您正在寻找性能更高的解决方案,您可以动态生成 "hardcoded" 版本的源代码并使用 Function
- see this answer 进行编译作为示例。
您如何实施 Range
class?我尝试了这项工作:
class Range{
constructor(){
var args = Array.prototype.slice.call(arguments);
this.getValues = function*(){
for(const num of args){
yield num;
}
};
}
}
也可以使用数组:
'use strict';
const paramDef = {
propA: [1, 2, 3], // [1,2,3] as iterator
propB: [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){
// and foreach value of propB...
for(let valPropB of def.propB){
// 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);
}
所以,我认为问题出在您的 Range
class 实施上。
这是主要功能(完整代码如下):
// 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);
}
}
我找到的最佳资源是 MDN's Example with yield*
演示。如果你想更好地理解 ES6 生成器,那整篇文章绝对值得一读。
感谢@Bergi 指出 yield
ed 对象实例在所有情况下都是相同的,理想情况下应该在每个分支上克隆它(因此它们都是不同的实例)。
整个示例都包含在此代码段中(运行 看看结果)。
// 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...
我创建了一个 Range
class,它创建了一个生成器函数,用于迭代一个整数范围。我的下一步是创建一个生成器函数,为每个 属性 迭代所有可能的值排列。这是硬编码示例的简化代码:
// 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}
我已经尝试了很多事情,但我得到的最深入的是将第一次迭代正确地 return,但没有别的。您如何概括 getUnits()
函数以及我应该注意哪些陷阱?
您可以对 属性 个名称的列表使用递归:
function getObjectsOf(def) {
var keys = Object.keys(def),
o = {};
return rec(keys.length);
function* rec(i) {
if (i <= 0) {
let clone = {}; // I assume you want to yield different objects
for (let k of keys) // or: k in o
clone[k] = o[k];
yield clone;
} else {
let key = keys[i];
for (let value of def[key]) {
o[key] = value;
yield* rec(i-1);
}
}
}
}
如果您正在寻找性能更高的解决方案,您可以动态生成 "hardcoded" 版本的源代码并使用 Function
- see this answer 进行编译作为示例。
您如何实施 Range
class?我尝试了这项工作:
class Range{
constructor(){
var args = Array.prototype.slice.call(arguments);
this.getValues = function*(){
for(const num of args){
yield num;
}
};
}
}
也可以使用数组:
'use strict';
const paramDef = {
propA: [1, 2, 3], // [1,2,3] as iterator
propB: [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){
// and foreach value of propB...
for(let valPropB of def.propB){
// 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);
}
所以,我认为问题出在您的 Range
class 实施上。
这是主要功能(完整代码如下):
// 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);
}
}
我找到的最佳资源是 MDN's Example with yield*
演示。如果你想更好地理解 ES6 生成器,那整篇文章绝对值得一读。
感谢@Bergi 指出 yield
ed 对象实例在所有情况下都是相同的,理想情况下应该在每个分支上克隆它(因此它们都是不同的实例)。
整个示例都包含在此代码段中(运行 看看结果)。
// 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...