递归地将对象字段从蛇形命名法转换为驼峰命名法

14
我有一个对象,格式如下(字段使用蛇形命名):
const obj = {
  vt_core_random: {
    user_details: {
      first_name: "xyz",
      last_name: "abc",
      groups: [
        {
          id: 1,
          group_type: "EXT"
        },
        {
          id: 2,
          group_type: "INT"
        }
      ],
      address_type: {
        city_name: "nashik",
        state: {
          code_name: "MH",
          name: "Maharashtra"
        }
      }
    }
  }
};

我希望能够递归地将其字段转换为 驼峰式,因此期望的输出如下所示

const obj = {
  vtCoreRandom: {
    userDetails: {
      firstName: "xyz",
      lastName: "abc",
      groups: [
        {
          id: 1,
          groupType: "EXT"
        },
        {
          id: 2,
          groupType: "INT"
        }
      ],
      addressType: {
        cityName: "LMN",
        state: {
          codeName: "KOP",
          name: "PSQ"
        }
      }
    }
  }
};

我尝试使用mapKeys(),但我就是无法理解其中的递归部分。非常感谢任何帮助。如果使用lodash可以使这个过程更加简单,我也可以使用它。


简短的,不带库的JavaScript/TypeScript ES6示例:https://dev59.com/Wrjoa4cB1Zd3GeqPCq0V#75927783 - leonheess
11个回答

28
你可以使用lodash的_.transform()创建一个递归函数,该函数迭代键并使用_.camelCase()将它们转换为驼峰式。Transform也可以处理数组,所以如果迭代对象(target)是一个数组,我们就不需要更改键。

const camelize = obj => _.transform(obj, (acc, value, key, target) => {
  const camelKey = _.isArray(target) ? key : _.camelCase(key);
  
  acc[camelKey] = _.isObject(value) ? camelize(value) : value;
});

const obj = {"vt_core_random":{"user_details":{"first_name":"xyz","last_name":"abc","groups":[{"id":1,"group_type":"EXT"},{"id":2,"group_type":"INT"}],"address_type":{"city_name":"nashik","state":{"code_name":"MH","name":"Maharashtra"}}}}};

const result = camelize(obj);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>


3
Typescript版本:import { camelCase, isArray, transform, isObject } from "lodash"; const camelize = (obj: Record) => transform(obj, (result: Record, value: unknown, key: string, target) => { const camelKey = isArray(target) ? key : camelCase(key); result[camelKey] = isObject(value) ? camelize(value as Record) : value; }); - Juice10

15

你可以尝试像这样做:

const obj = {
  vt_core_random: {
    user_details: {
      first_name: "xyz",
      last_name: "abc",
      groups: [
        {
          id: 1,
          group_type: "EXT"
        },
        {
          id: 2,
          group_type: "INT"
        }
      ],
      address_type: {
        city_name: "nashik",
        state: {
          code_name: "MH",
          name: "Maharashtra"
        }
      }
    }
  }
};

const toCamel = (str) => {
  return str.replace(/([-_][a-z])/ig, ($1) => {
    return $1.toUpperCase()
      .replace('-', '')
      .replace('_', '');
  });
};

const isObject = function (obj) {
  return obj === Object(obj) && !Array.isArray(obj) && typeof obj !== 'function';
};

const keysToCamel = function (obj) {
  if (isObject(obj)) {
    const n = {};

    Object.keys(obj)
      .forEach((k) => {
        n[toCamel(k)] = keysToCamel(obj[k]);
      });

    return n;
  } else if (Array.isArray(obj)) {
    return obj.map((i) => {
      return keysToCamel(i);
    });
  }
  
  return obj;
};

console.log(keysToCamel(obj));


你能解释一下这里 $ 操作符的用法吗? - nonNumericalFloat

7

纯JavaScript

(适用于蛇形命名和短横线命名)

const recursiveToCamel = item => {
  if (Array.isArray(item)) {
    return item.map(el => recursiveToCamel(el));
  } else if (typeof item === 'function' || item !== Object(item)) {
    return item;
  }
  return Object.fromEntries(
    Object.entries(item).map(([key, value]) => [
      key.replace(/([-_][a-z])/gi, c => c.toUpperCase().replace(/[-_]/g, '')),
      recursiveToCamel(value),
    ]),
  );
};

以下是使用本问题提供的对象的小型演示:

const obj = {
  vt_core_random: {
    user_details: {
      first_name: "xyz",
      last_name: "abc",
      groups: [{
          id: 1,
          group_type: "EXT"
        },
        {
          id: 2,
          group_type: "INT"
        }
      ],
      address_type: {
        city_name: "nashik",
        state: {
          code_name: "MH",
          name: "Maharashtra"
        }
      }
    }
  }
};

const recursiveToCamel = item => {
  if (Array.isArray(item)) {
    return item.map(el => recursiveToCamel(el));
  } else if (typeof item === 'function' || item !== Object(item)) {
    return item;
  }
  return Object.fromEntries(
    Object.entries(item).map(([key, value]) => [
      key.replace(/([-_][a-z])/gi, c => c.toUpperCase().replace(/[-_]/g, '')),
      recursiveToCamel(value),
    ]),
  );
};

console.log(recursiveToCamel(obj));

类型Script

const recursiveToCamel = (item: unknown): unknown => {
  if (Array.isArray(item)) {
    return item.map((el: unknown) => recursiveToCamel(el));
  } else if (typeof item === 'function' || item !== Object(item)) {
    return item;
  }
  return Object.fromEntries(
    Object.entries(item as Record<string, unknown>).map(
      ([key, value]: [string, unknown]) => [
        key.replace(/([-_][a-z])/gi, c => c.toUpperCase().replace(/[-_]/g, '')),
        recursiveToCamel(value),
      ],
    ),
  );
};

6

对于所有使用lodash的人,我已经修改了被接受的答案,因为lodash已经包含了实用函数,如isArray isObject camelCase

所以代码减少到了这个。:)

function keysToCamel(obj) {
  if (isPlainObject(obj)) {
    const n = {};
    Object.keys(obj).forEach(k => (n[camelCase(k)] = keysToCamel(obj[k])));
    return n;
  } else if (isArray(obj)) obj.map(i => keysToCamel(i));
  return obj;
}

1
这不起作用是因为lodash中的isObject对于数组返回true。检查值是否为语言类型的对象。(例如,数组,函数,对象,正则表达式,new Number(0)和new String('')) - cip123
1
Typescript版本: function convertKeysToCamelCase(obj: unknown): unknown { if (isPlainObject(obj)) { const n = {}; Object.keys(obj as object).forEach( (k) => (n[camelCase(k)] = convertKeysToCamelCase((obj as object)[k])) ); return n; } else if (isArray(obj)) obj.map((i) => convertKeysToCamelCase(i)); return obj; } - Juice10
if else 语句中,我们应该返回映射后的数组以使其能够处理对象数组,否则,数组内部的对象将不会被转换。 - Abdelaziz Mokhnache

3

对于那些使用 TypeScript 的人,我在这个问题的接受答案中添加了类型定义。

const toCamel = (str: string): string => {
  return str.replace(/([_-][a-z])/gi, ($1: string) => {
    return $1.toUpperCase().replace('-', '').replace('_', '');
  });
};

const isArray = function (
  input: Record<string, unknown> | Record<string, unknown>[] | unknown
): input is Record<string, unknown>[] {
  return Array.isArray(input);
};

const isObject = function (
  obj: Record<string, unknown> | Record<string, unknown>[] | unknown
): obj is Record<string, unknown> {
  return (
    obj === Object(obj) && !Array.isArray(obj) && typeof obj !== 'function'
  );
};

const camelize = function <T>(input: T): T {
  return (function recurse<
    K extends Record<string, unknown> | Record<string, unknown>[] | unknown
  >(input: K): K {
    if (isObject(input)) {
      return Object.keys(input).reduce((acc, key) => {
        return Object.assign(acc, { [toCamel(key)]: recurse(input[key]) });
      }, {} as K);
    } else if (isArray(input)) {
      return input.map((i) => recurse(i)) as K;
    }
    return input;
  })(input);
};

2
你可以以一种相对通用的方式来实现这个功能,编写一个接受任意键转换函数的函数,并返回一个接受对象并返回具有相同结构但键已转换的对象。这基本上和为驼峰命名键编写代码一样简单。
以下是一种方法:

const fixKeys = (fn) => (obj) => Object .fromEntries (
  Object .entries (obj) .map (([k, v]) => [
    fn(k), 
    Array .isArray (v) ? v .map (fixKeys (fn)) : Object (v) === v ? fixKeys (fn) (v) : v
  ])
)

const camelCase = (s) => s.replace(/_(.)/g, (s, c) => c.toUpperCase())

const camelizeKeys = fixKeys (camelCase)

const obj = {vt_core_random: {user_details: {first_name: "xyz", last_name: "abc", groups: [{id: 1, group_type: "EXT"}, {id: 2, group_type: "INT"}], address_type: { city_name: "nashik", state: {code_name: "MH",  name: "Maharashtra"}}}}}

console .log (camelizeKeys (obj))
.as-console-wrapper {max-height: 100% !important; top: 0}

如果您的环境中不可用Object.fromEntries,那么很容易进行模拟或替换。此外,这个标签是“lodash”,如果您已经在使用它,您可能希望用lodash函数替换这里的一些自定义功能,包括camelCase函数以及数组和对象测试。当然,这已经相当简单了。

如果属性的值为null,会有一个小问题,因为null类型是'object'。根据leonheess的答案,我们可以将对象检查反转为:v !== Object(v) ? v : fixKeys(fn)(v) - Marcelo Cristiano Araujo Silva
@Marcelo:你说得对。在过去的几年里,我开始使用这个代替typeof。我已经更新了答案,包括了它。 - Scott Sauyet

1
const obj = {
  vt_core_random: {
    user_details: {
      first_name: "xyz",
      last_name: "abc",
      groups: [
        {
          id: 1,
          group_type: "EXT"
        },
        {
          id: 2,
          group_type: "INT"
        }
      ],
      address_type: {
        city_name: "nashik",
        state: {
          code_name: "MH",
          name: "Maharashtra"
        }
      }
    }
  }
};

function toCamel(o) {
  var newO, origKey, newKey, value
  if (o instanceof Array) {
    return o.map(function(value) {
        if (typeof value === "object") {
          value = toCamel(value)
        }
        return value
    })
  } else {
    newO = {}
    for (origKey in o) {
      if (o.hasOwnProperty(origKey)) {
        newKey = _.camelCase(origKey)
        value = o[origKey]
        if (value instanceof Array || (value !== null && value.constructor === Object)) {
          value = toCamel(value)
        }
        newO[newKey] = value
      }
    }
  }
  return newO
}

console.log(toCamel(obj));    const obj = {
  vt_core_random: {
    user_details: {
      first_name: "xyz",
      last_name: "abc",
      groups: [
        {
          id: 1,
          group_type: "EXT"
        },
        {
          id: 2,
          group_type: "INT"
        }
      ],
      address_type: {
        city_name: "nashik",
        state: {
          code_name: "MH",
          name: "Maharashtra"
        }
      }
    }
  }
};

function toCamel(o) {
  var newO, origKey, newKey, value
  if (o instanceof Array) {
    return o.map(function(value) {
        if (typeof value === "object") {
          value = toCamel(value)
        }
        return value
    })
  } else {
    newO = {}
    for (origKey in o) {
      if (o.hasOwnProperty(origKey)) {
        newKey = _.camelCase(origKey)
        value = o[origKey]
        if (value instanceof Array || (value !== null && value.constructor === Object)) {
          value = toCamel(value)
        }
        newO[newKey] = value
      }
    }
  }
  return newO
}

console.log(toCamel(obj));

我在这段代码中使用了 lodash 库。


0

我使用了Donny Verduijn的代码,但发现Date对象被转换为{}

我还需要能够从snakeToCamel和camelToSnake进行转换的能力,因此我添加了一个格式化参数。

export const snakeToCamel = (str: string): string =>
  str.replace(/([_-][a-z])/gi, ($1: string) => $1.toUpperCase().replace('-', '').replace('_', ''));

export const camelToSnake = (str: string): string =>
  str.replace(/([A-Z])/g, ($1: string) => `_${$1.toLowerCase()}`);

const isArray = function (
  input: Record<string, unknown> | Record<string, unknown>[] | unknown
): input is Record<string, unknown>[] {
  return Array.isArray(input);
};

export const isObject = function (
  obj: Record<string, unknown> | Record<string, unknown>[] | unknown
): obj is Record<string, unknown> {
  return (
    isValidDate(obj) === false &&
    obj === Object(obj) &&
    !Array.isArray(obj) &&
    typeof obj !== 'function'
  );
};

const isValidDate = (value: any) => value instanceof Date;

const modifyObjectKeys = function <T>(input: T, formatter: (word: string) => string): T {
  return (function recurse<K extends Record<string, unknown> | Record<string, unknown>[] | unknown>(
    input: K
  ): K {
    if (isObject(input)) {
      return Object.keys(input).reduce(
        (acc, key) => Object.assign(acc, { [formatter(key)]: recurse(input[key]) }),
        {} as K
      );
    } else if (isArray(input)) {
      return input.map((i) => recurse(i)) as K;
    }
    return input;
  })(input);
};

/**
 *
 * @param input Object to convert keys to camelCase
 * @returns Object with keys converted to camelCase
 */
export const camelize = function <T>(input: T): T {
  return modifyObjectKeys(input, snakeToCamel);
};

/**
 *
 * @param input Object to convert keys to snake_case
 * @returns Object with keys converted to snake_case
 */
export const snakeify = function <T>(input: T): T {
  return modifyObjectKeys(input, camelToSnake);
};


0
使用 npm 的 json-case-handler 可以让你在一行代码中实现此功能。
它可以转换任何嵌套对象。
对于你的情况,你可以这样做:
const jcc = require('json-case-convertor')
const camelCasedJson = jcc.camelCaseKeys(yourjsonData)

0

使用lodash/fp的函数式编程方法解决方案

import { camelCase, toPairs, fromPairs, map, compose, isArray, isObject } from 'lodash/fp';

const convertAllKeysToCamelCase = compose(
  fromPairs,
  map(([key, value]) => {
    if (isArray(value)) {
      return [camelCase(key), map(convertAllKeysToCamelCase, value)];
    }

    if (isObject(value)) {
      return [camelCase(key), convertAllKeysToCamelCase(value)];
    }

    return [camelCase(key), value];
  }),
  toPairs
);

convertAllKeysToCamelCase(myObj)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接