如果从对象中取一部分属性,请问有什么现成的轮子
发布于 5 年前 作者 DiamondYuan 2511 次浏览 来自 问答

lodash 有 pick 这个方法

var object = { 'a': 1, 'b': '2', 'c': 3 };
 
_.pick(object, ['a', 'c']);
// => { 'a': 1, 'c': 3 }

但是 object 如果是一个嵌套的对象的话,就要写很多代码。 我自己写了一个 deeppick 的轮子。

const api: OpenAPIObject = {
  openapi: "3.0.1",
  info: {
    version: "1.0",
    title: "test-rest",
    description: "swagger3.0.1 api docs",
  },
  paths: {
    "api/v1/cases": {
      get: {
        tags: ["case-controller"],
        summary: "获取病历列表",
        parameters: [
          {
            name: "department_id",
            in: "query",
            description: "科室ID",
            required: false,
            schema: {
              type: "integer",
              format: "int32",
            },
          },
          {
            name: "patient_name",
            in: "query",
            description: "姓名",
            required: false,
            schema: {
              type: "string",
            },
          },
        ],
      },
    },
  },
};

const objectPicker = {
  paths: new MapPicker({
    get: {
      summary: true,
      parameters: new ArrayPicker({
        in: true,
        required: true,
      }),
    },
  }),
};

const expectResult = {
  paths: {
    "api/v1/cases": {
      get: {
        summary: "获取病历列表",
        parameters: [
          { in: "query", required: false },
          { in: "query", required: false },
        ],
      },
    },
  },
};

expect(deepPick(api, objectPicker)).toEqual(expectResult);

函数效果如上。 虽然我自己写好了,但是我觉得应该前人已经造过现成的轮子了。请问有人见过类似的轮子吗?

5 回复
import { plainToClass, Type, Exclude, Expose } from "class-transformer";
import "reflect-metadata";


const api = {
    openapi: "3.0.1",
    info: {
        version: "1.0",
        title: "test-rest",
        description: "swagger3.0.1 api docs",
    },
    paths: {
        "api/v1/cases": {
            get: {
                tags: ["case-controller"],
                summary: "获取病历列表",
                parameters: [
                    {
                        name: "department_id",
                        in: "query",
                        description: "科室ID",
                        required: false,
                        schema: {
                            type: "integer",
                            format: "int32",
                        },
                    },
                    {
                        name: "patient_name",
                        in: "query",
                        description: "姓名",
                        required: false,
                        schema: {
                            type: "string",
                        },
                    },
                ],
            },
        },
    },
};

@Exclude()
class Parameters {

    @Expose()
    in: string;

    @Expose()
    required: boolean;

}

@Exclude()
class Get {

    @Expose()
    summary: string;

    @Expose()
    @Type(() => Parameters)
    parameters: Parameters[];

}

@Exclude()
class Api {

    @Expose()
    @Type(() => Get)
    get: Get;
}

@Exclude()
class Paths {

    @Expose()
    @Type(() => Api)
    ['api/v1/cases']: Api;
}

@Exclude()
class Template {

    @Expose()
    @Type(() => Paths)
    paths: Paths;

}


var json = plainToClass(Template, api);

console.log(JSON.stringify(json));

用 class-transformer 得定义类型,比较麻烦,如果不用 ts, 使用 ramda mergeDeep 相关的操作符 应该只需要写 7-8 行

@yviscool 有些情况下map的key是未知的,例如 api/v1/cases 只是举个例子,并不知道有多少API。后来我自己写了一个

export function deepPick(origin: any, keys: Picker) {
  if (!origin) {
    return undefined;
  }
  const result: any = {};
  Object.keys(keys).forEach((o: string) => {
    const valuePicker = keys[o];
    let pickResult: any = {};
    const originValue = origin[o];
    if (!originValue) {
      return;
    }
    if (valuePicker === true) {
      pickResult = origin[o];
    } else if (valuePicker instanceof MapPicker) {
      valuePicker.filter(originValue).forEach((subKey: string) => {
        pickResult[subKey] = deepPick(
          originValue[subKey],
          valuePicker.getPicker(),
        );
      });
    } else if (valuePicker instanceof ArrayPicker) {
      pickResult = valuePicker
        .filter(originValue)
        .map((subObject) => deepPick(subObject, valuePicker.getPicker()));
    } else if (valuePicker instanceof Object) {
      pickResult = deepPick(origin[o], valuePicker);
    }
    result[o] = pickResult;
  });
  return result;
}

export interface Picker {
  [key: string]: MapPicker | ArrayPicker | true | Picker;
}

临时写的,暂时够用,测试也没补全。

@DiamondYuan key未知,那么这个库做不到(只能把所有属性都写上)。等等看别人有没什么高招

递归判断就可以了吧。

ECMA-262的一个处于Stage1的草案未来可能会很好解决这个问题: https://github.com/tc39/proposal-optional-chaining/blob/master/README.md

回到顶部