Привет, Хабр! Меня зовут Владимир, работаю в Ozon, занимаюсь фронтендом.   

Сегодня мы поговорим о строительстве мостов взаимопонимания между фронтендом и бэкендом – в той части, которая связана со стилем написания переменных. 

Представим ситуацию: начинается работа над сайтом, разработчики тёмной и светлой сторон встречаются обсудить насущные вопросы. Один из таких вопросов связан с передачей данных. 

Бекенд отдает и принимает данные в виде: 

{ 
		user_name: "user1", 
		main_title: "Title", 
} 

Фронтенд:

{ 
		userName: "user1", 
		mainTitle: "Title", 
} 

В итоге выбор стиля написания переменных может привести к горячему спору, а иногда и к небольшой потасовке.

В этой статье мы попробуем решить эту проблему – преобразовать все данные бэкенда в данные фронтенда и наоборот. Воспользуемся для этого JavaScript. 

Надеюсь, статья будет полезна начинающим разработчикам, а остальным лишний раз напомнит о знакомых приёмах по добавлению комфорта в разработку. 

Шаг 1. Преобразование строки 

Нам поможет встроенная функция replace. Она умеет заменять каждое вхождение заданного регулярного выражения с помощью функции маппера, которую мы передаём вторым аргументом. 

# Преобразование snake_keys строки в camelKeys: 

const snakeToCamel = str => { 
    return str.replace(/([_][a-z])/g, letter => { 
        return letter 
                .toUpperCase() 
                .replace('_', '') 
    }) 
} 

# Преобразование camelKeys строки в snake_keys: 

const camelToSnake = str => { 
    return str.replace(/[A-Z]/g, letter => { 
        return '_' + letter.toLowerCase() 
    }) 
} 

Шаг 2. Работа с объектами 

# Возьмем пример с начала статьи 

{ 
		user_name: "user1", 
		main_title: "Title", 
} 

Пройдёмся по ключам объекта и заменим их с помощью уже реализованной функции snakeToCamel. 

const simpleKeysTransform = value => { 
    return Object.entries(value).reduce((acc, [key, value]) => { 
        const newKey = snakeToCamel(key) 
        
        return {...acc, [newKey]: value} 
    }, {}) 
} 

Давайте теперь сделаем универсальную функцию преобразования и будем принимать на вход ещё переменную, отвечающую за изначальный вид стиля ключей объекта.

const keysTransform1 = (value, isInitialSnake = true) => { 
    const chooseStyle = isInitialSnake ? snakeToCamel : camelToSnake 

    return Object.entries(value).reduce((acc, [key, value]) => { 
        const newKey = chooseStyle(key) 

        return {...acc, [newKey]: value} 
    }, {}) 
} 

Шаг 3. Что делать с вложенными объектами 

# Например 

{ 
  user_info: { 
    first_name: "User", 
    last_name: "Userin” 
  } 
} 

Применим рекурсию. Обернём основную логику в функцию и в ней будем проверять: является ли наше значение объектом. Если да, то будем вызывать нашу функцию снова и снова. 

const keysTransform2 = (input, isInitialSnake = true) => { 
    const chooseStyle = isInitialSnake ? snakeToCamel : camelToSnake 

    const recursiveTransform = value => { 

        if (value && typeof value === 'object') { 
            return Object.entries(value).reduce((acc, [key, value]) => { 
                const newKey = chooseStyle(key) 
                const newValue = recursiveTransform(value) 

                return {...acc, [newKey]: newValue} 
            }, {}) 
        } 

        return value 
    } 

    return recursiveTransform(input) 
} 

Шаг 4. Что делать с массивами 

# Например 

{ 
  users: [ 
    { 
      first_name: "user1", 
      phone_number: 8996923 
    }, 
    { 
      first_name: "user2", 
      phone_number: 12312312 
    } 
  ]   
} 

Всё до безобразия просто. Добавим проверку на массив и на каждый его элемент навесим нашу рекурсивную функцию. 

const keysTransform = (input, isInitialSnake = true) => { 

    const chooseStyle = isInitialSnake ? snakeToCamel : camelToSnake 

    const recursiveTransform = value => { 
        if (Array.isArray(value)) { 
            return value.map(recursiveTransform) 
        } 

        if (value && typeof value === 'object') { 
            return Object.entries(value).reduce((acc, [key, value]) => { 
                const newKey = chooseStyle(key) 
                const newValue = recursiveTransform(value) 

                return {...acc, [newKey]: newValue} 
            }, {}) 
        } 

        return value 
    } 

    return recursiveTransform(input) 
} 

Перемирие 

Давайте посмотрим, что получилось: мы реализовали алгоритм преобразования ключей объектов из snake_keys в camelKeys и наоборот. Чуть-чуть меньше раздора между фронтендом и бэкендом – неплохо же! 

Существуют и другие стили написания составных слов (PascalKeys, kebab-keys, UPPER_SNAKE_KEYS). При надобности, вы уже сами сможете с ними справиться.