【问题标题】:iterate through an array of object based property path遍历基于对象的属性路径数组
【发布时间】:2021-04-11 19:21:56
【问题描述】:

您好,我在迭代 SubjectViewModel 时需要帮助,并检查 formfieldkeyPath 的路径是否与数组中每个对象的 PropertyKey 匹配,如果属性路径值为 null,则仅返回 true。

例如 formfieldkeyPath,它是一个值为 department.subjectCost 的字符串

export class SubjectViewModel {
    public id?: number;
    public SubjectInfo?: string;
    public studentId?: number;
    public department?:DepartmentViewModel;
    public studentBenefits?: studentBenefitViewModel[];
        

}

export class DepartmentViewModel{
    public id?: number;
    public departmentName?:string;
    public subjectCost?:number;
    public departmentBenefits?: departmentBenefitViewModel[];
        

}

SubjectViewModel =[
 {
   id:4,
   SubjectInfo:'xx',
   studentId:16,
   department:[
   {
     id:1,
     departmentName:'xxx',
     subjectCost:null,
     departmentBenefits:[{..},{..}]
   },
   {
     id:2,
     departmentName:'yyy',
     subjectCost:50,
     departmentBenefits:[{..},{..}]
   }],
   studentBenefits:[{..},{..}]
 }
]


i split the department.subjectCost into an array
const propertyPathArr = formfieldkeyPath .split('.'),


【问题讨论】:

  • 尝试使用 Object.keys() 并将其与您的路径进行比较。
  • 你能说清楚你想比较哪两个属性吗?我认为它是 SubjectCost 与列表中的其他内容!
  • 每个对象的PropertyKeyformfieldkeyPath 是什么?为什么SubjectViewModel 是一个数组?不是SubjectViewModel类的实例吗?
  • 另外举个例子,你的预期输出是什么? SubjectViewModel 是你的意见吗?

标签: javascript arrays angular typescript


【解决方案1】:

我也不得不这样做几次。您已经非常接近按期间拆分了。从这里开始,您只需要使用括号表示法迭代到由该字符串确定的每个深度级别,然后使用 map() 返回匹配属性值的新数组。

我在您尝试检索的数据和您要使用的 formfieldkeyPath (department.subjectCost) 之间看到的唯一复杂之处是 department 是您要迭代的区域,而 subjectCost 是数据我们希望通过map() 收集。这将在哪些属性路径“部分”是要遍历的数组以及循环遍历该数组时要收集的数据方面留下一些歧义。

要解决这个问题,我们需要使用稍微不同的符号来分隔这两个部分,即要循环遍历的数组的路径,以及要在该数组中收集的属性的路径。本质上,我们将构建两个填充了路径名的嵌套数组,然后遍历这些属性路径名以获取所需的数据。

例如,如果您的对象嵌套在另外两个对象objsubobj 中,我们可以将obj.subobj.department->subjectCost 转换为[['obj','subobj','department'],['subjectCost']]。因此,为了让这适用于您更简单的示例,我们将只使用 department->subjectCost,因为我们需要为复杂度更高的对象保存 . 字符。

** 这里的另一个重要注意事项是,因为我们将使用符号 .-> 来拆分我们的字符串,这将与在其属性名称中实际包含这些确切字符串的任何对象键冲突,所以要小心。如果您确实有名称中包含其中任何一个符号的属性,只需更改我在下面提供的函数中使用的符号。也许您可以使用..... 而不是.->,其中两个点代表一条路径(向下一层),三个点代表数组路径和指向您正在收集的所需数据。在我们的例子中,它是department...subjectCost

示例 1:使用您提供的示例数据

使用您提供的示例数据(.->),department->subjectCost 正在发挥作用:

用法

iterPropPath(SubjectViewModel[0], 'department->subjectCost');
// -> [null, 50]

完整示例

const SubjectViewModel = [{
  id: 4,
  SubjectInfo: 'xx',
  studentId: 16,
  department: [{
      id: 1,
      departmentName: 'xxx',
      subjectCost: null,
      departmentBenefits: [{/**/}, {/**/}]
    }, {
      id: 2,
      departmentName: 'yyy',
      subjectCost: 50,
      departmentBenefits: [{/**/}, {/**/}]
  }],
  studentBenefits: [{/**/}, {/**/}]
}];

const iterPropPath = (obj, path) => {
  path = path.replace(/\[(\w+)\]/g, '.$1').replace(/^\./, '');
  const pathArray = path.split('->').map(e => e.split('.').filter(f => f)).filter(e => e && e.length);
  if (pathArray.length !== 2) {
    console.error('iterPropPath requires a valid object path to a nested array and a valid path within that array to the desired property value. Property path levels should be delimited by `.` and the array path/property path should be delimited by `->`.');
    return false;
  }
  const [arrPath, propPath] = pathArray;
  let arr = obj;
  let arrPathValid = true;
  arrPath.slice().forEach(step => arr.hasOwnProperty(step) ? (arr = arr[arrPath.shift(1)], !arrPath.length && (!Array.isArray(arr) || !arr.length) && (arrPathValid = false)) : (arrPathValid = false));
  if (!arrPathValid) {
    console.error(`Array path ${arrPath.join('.')} is invalid.`);
    return false;
  }
  return arr.map((e,iPropPath) => (iPropPath = propPath.slice(), propPath.forEach(step => e && e.hasOwnProperty(step) && (e = e[iPropPath.shift(1)])), e));
};

const formfieldkeyPath = 'department->subjectCost';
const subjectCosts = iterPropPath(SubjectViewModel[0], formfieldkeyPath);

console.log(subjectCosts);

这将沿着提供的对象 SubjectViewModel[0] 的路径向下到达数组的指定路径,然后遍历数组,返回所需值的新映射数组,在本例中为 [null, 50]subjectCost 值)。

示例 2:更复杂的示例

下面是这个函数的一个更深入的例子,它使用括号表示法。为了支持括号表示法,我在 2011 年的一个相关问题中添加了一些受 @Alnitak 的 answer 启发的 replace 语句:

用法

iterPropPath(deepArray, 'entries.data->a[1].names[2]');
// -> ["Ron", "Eli", "Bre"]

完整示例

const deepArray = {
  id: 0,
  entries: {
    randomData: true,
    data: [
      { a: ['test1-1', { names: [ 'Tom', 'Kim', 'Ron' ] }], b: [['test1-2'], ['test1-3']]},
      { a: ['test2-1', { names: [ 'Jim', 'Lia', 'Eli' ] }], b: [['test2-2'], ['test2-3']]},
      { a: ['test3-1', { names: [ 'Tia', 'Jon', 'Bre' ] }], b: [['test3-2'], ['test3-3']]}
    ]
  }
};

const iterPropPath = (obj, path) => {
  path = path.replace(/\[(\w+)\]/g, '.$1').replace(/^\./, '');
  const pathArray = path.split('->').map(e => e.split('.').filter(f => f)).filter(e => e && e.length);
  if (pathArray.length !== 2) {
    console.error('iterPropPath requires a valid object path to a nested array and a valid path within that array to the desired property value. Property path levels should be delimited by `.` and the array path/property path should be delimited by `->`.');
    return false;
  }
  const [arrPath, propPath] = pathArray;
  let arr = obj;
  let arrPathValid = true;
  arrPath.slice().forEach(step => arr.hasOwnProperty(step) ? (arr = arr[arrPath.shift(1)], !arrPath.length && (!Array.isArray(arr) || !arr.length) && (arrPathValid = false)) : (arrPathValid = false));
  if (!arrPathValid) {
    console.error(`Array path ${arrPath.join('.')} is invalid.`);
    return false;
  }
  return arr.map((e,iPropPath) => (iPropPath = propPath.slice(), propPath.forEach(step => e && e.hasOwnProperty(step) && (e = e[iPropPath.shift(1)])), e));
};

const path = 'entries.data->a[1].names[2]';
const namesFromDeepArray = iterPropPath(deepArray, path);

console.log(namesFromDeepArray);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-08
    • 1970-01-01
    • 1970-01-01
    • 2014-08-09
    相关资源
    最近更新 更多