← Назад к вопросам
Flatten вложенного объекта
2.2 Middle🔥 121 комментариев
#Node.js и JavaScript#Алгоритмы и структуры данных
Условие
Напишите функцию flattenObject, которая преобразует вложенный объект в плоский, используя точечную нотацию для ключей:
function flattenObject(obj, prefix = "") {
// Ваш код
}
// Пример:
const input = {
a: 1,
b: {
c: 2,
d: {
e: 3
}
},
f: 4
};
console.log(flattenObject(input));
// {
// "a": 1,
// "b.c": 2,
// "b.d.e": 3,
// "f": 4
// }
Что проверяется
- Рекурсия
- Работа с объектами
- Итерация по свойствам объекта
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение
Базовая реализация
function flattenObject(
obj: Record<string, any>,
prefix: string = ""
): Record<string, any> {
const result: Record<string, any> = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = obj[key];
const newKey = prefix ? `${prefix}.${key}` : key;
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
// Рекурсивно обрабатываем вложенные объекты
Object.assign(result, flattenObject(value, newKey));
} else {
// Добавляем примитивные значения
result[newKey] = value;
}
}
}
return result;
}
Как это работает
Принцип работы:
- Итерируем по ключам — проходим по каждому свойству объекта
- Формируем полный ключ — собираем путь через точку (a.b.c)
- Проверяем значение — если объект, рекурсивно разворачиваем
- Добавляем в результат — примитивные значения добавляем сразу
Пошаговый процесс для примера:
Вход: { a: 1, b: { c: 2, d: { e: 3 } }, f: 4 }
Шаг 1: Ключ "a"
├─ value = 1 (примитив)
├─ newKey = "a"
└─ result["a"] = 1
Шаг 2: Ключ "b"
├─ value = { c: 2, d: { e: 3 } } (объект)
├─ newKey = "b"
├─ Рекурсия: flattenObject(value, "b")
│ ├─ Ключ "c"
│ │ ├─ newKey = "b.c"
│ │ └─ result["b.c"] = 2
│ └─ Ключ "d"
│ ├─ value = { e: 3 } (объект)
│ ├─ newKey = "b.d"
│ ├─ Рекурсия: flattenObject(value, "b.d")
│ │ └─ result["b.d.e"] = 3
Шаг 3: Ключ "f"
├─ value = 4 (примитив)
├─ newKey = "f"
└─ result["f"] = 4
Результат: { "a": 1, "b.c": 2, "b.d.e": 3, "f": 4 }
Пример использования
const input = {
a: 1,
b: {
c: 2,
d: {
e: 3
}
},
f: 4
};
const flattened = flattenObject(input);
console.log(flattened);
// {
// "a": 1,
// "b.c": 2,
// "b.d.e": 3,
// "f": 4
// }
Версия с поддержкой массивов
function flattenObject(
obj: Record<string, any>,
prefix: string = ""
): Record<string, any> {
const result: Record<string, any> = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = obj[key];
const newKey = prefix ? `${prefix}.${key}` : key;
if (Array.isArray(value)) {
// Обрабатываем массивы
value.forEach((item, index) => {
const arrayKey = `${newKey}[${index}]`;
if (typeof item === "object" && item !== null) {
Object.assign(result, flattenObject(item, arrayKey));
} else {
result[arrayKey] = item;
}
});
} else if (value !== null && typeof value === "object") {
// Рекурсивно обрабатываем объекты
Object.assign(result, flattenObject(value, newKey));
} else {
// Добавляем примитивные значения
result[newKey] = value;
}
}
}
return result;
}
// Пример:
const input = {
a: 1,
b: [
{ c: 2 },
{ c: 3 }
]
};
flattenObject(input);
// {
// "a": 1,
// "b[0].c": 2,
// "b[1].c": 3
// }
Версия с использованием reduce
function flattenObject(
obj: Record<string, any>,
prefix: string = ""
): Record<string, any> {
return Object.keys(obj).reduce((result, key) => {
const value = obj[key];
const newKey = prefix ? `${prefix}.${key}` : key;
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
return { ...result, ...flattenObject(value, newKey) };
}
return { ...result, [newKey]: value };
}, {});
}
Версия с поддержкой разделителя
function flattenObject(
obj: Record<string, any>,
separator: string = ".",
prefix: string = ""
): Record<string, any> {
const result: Record<string, any> = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = obj[key];
const newKey = prefix ? `${prefix}${separator}${key}` : key;
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
Object.assign(result, flattenObject(value, separator, newKey));
} else {
result[newKey] = value;
}
}
}
return result;
}
// Пример с разными разделителями:
const obj = { a: { b: { c: 1 } } };
flattenObject(obj, "."); // { "a.b.c": 1 }
flattenObject(obj, "_"); // { "a_b_c": 1 }
flattenObject(obj, "/"); // { "a/b/c": 1 }
Версия с обратным преобразованием (unflatten)
function unflattenObject(
obj: Record<string, any>,
separator: string = "."
): Record<string, any> {
const result: Record<string, any> = {};
for (const key in obj) {
const value = obj[key];
const keys = key.split(separator);
let current = result;
for (let i = 0; i < keys.length - 1; i++) {
const k = keys[i];
if (!(k in current)) {
current[k] = {};
}
current = current[k];
}
current[keys[keys.length - 1]] = value;
}
return result;
}
// Пример:
const flattened = { "a.b.c": 1, "a.b.d": 2 };
const unflattened = unflattenObject(flattened);
console.log(unflattened);
// { a: { b: { c: 1, d: 2 } } }
Тестирование
import { describe, it, expect } from 'vitest';
describe('flattenObject', () => {
it('должен разворачивать простой объект', () => {
const input = { a: 1, b: 2 };
const expected = { a: 1, b: 2 };
expect(flattenObject(input)).toEqual(expected);
});
it('должен разворачивать вложенный объект', () => {
const input = { a: 1, b: { c: 2, d: { e: 3 } }, f: 4 };
const expected = { a: 1, "b.c": 2, "b.d.e": 3, f: 4 };
expect(flattenObject(input)).toEqual(expected);
});
it('должен обрабатывать глубокие вложения', () => {
const input = { a: { b: { c: { d: { e: 1 } } } } };
const expected = { "a.b.c.d.e": 1 };
expect(flattenObject(input)).toEqual(expected);
});
it('должен обрабатывать разные типы значений', () => {
const input = {
str: "hello",
num: 42,
bool: true,
nil: null,
nested: { arr: [1, 2, 3] }
};
const result = flattenObject(input);
expect(result.str).toBe("hello");
expect(result.num).toBe(42);
expect(result.bool).toBe(true);
expect(result.nil).toBeNull();
expect(result["nested.arr"]).toEqual([1, 2, 3]);
});
it('должен обрабатывать пустые объекты', () => {
const input = { a: {}, b: 1 };
const result = flattenObject(input);
expect(result).toEqual({ b: 1 });
});
});
Практические применения
1. Конфигурационные файлы:
const config = {
database: {
host: "localhost",
port: 5432,
credentials: {
username: "admin",
password: "secret"
}
}
};
const flattened = flattenObject(config);
// {
// "database.host": "localhost",
// "database.port": 5432,
// "database.credentials.username": "admin",
// "database.credentials.password": "secret"
// }
2. Преобразование для API:
const userData = {
profile: {
name: "John",
contact: {
email: "john@example.com",
phone: "123-456-7890"
}
}
};
const formData = flattenObject(userData);
// Удобнее отправлять в URL-параметрах или форме
3. Логирование с контекстом:
const error = {
message: "Invalid input",
context: {
field: "email",
validation: {
rule: "required",
value: ""
}
}
};
const flattened = flattenObject(error);
console.log(flattened);
// Проще логировать структурированные данные
Сложность
- Временная сложность: O(n), где n — общее количество свойств
- Пространственная сложность: O(n), так как создаём новый объект
Альтернативный подход с рекурсией
function flattenObject(
obj: Record<string, any>,
prefix: string = "",
result: Record<string, any> = {}
): Record<string, any> {
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = obj[key];
const newKey = prefix ? `${prefix}.${key}` : key;
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
flattenObject(value, newKey, result);
} else {
result[newKey] = value;
}
}
}
return result;
}
Этот подход модифицирует результат в месте, что немного более эффективно по памяти.