Сжимающие иерархии объектов в JavaScript
Существует ли общий подход к "сжатию" вложенных объектов на один уровень:
var myObj = {
a: "hello",
b: {
c: "world"
}
}
compress(myObj) == {
a: "hello",
b_c: "world"
}
Я предполагаю, что будет какая-то рекурсия, но я подумал, что мне не нужно изобретать колесо здесь...!?
Ответы
Ответ 1
function flatten(obj, includePrototype, into, prefix) {
into = into || {};
prefix = prefix || "";
for (var k in obj) {
if (includePrototype || obj.hasOwnProperty(k)) {
var prop = obj[k];
if (prop && typeof prop === "object" &&
!(prop instanceof Date || prop instanceof RegExp)) {
flatten(prop, includePrototype, into, prefix + k + "_");
}
else {
into[prefix + k] = prop;
}
}
}
return into;
}
Вы можете включить элементы, наследуемые членами, передав true
во второй параметр.
Несколько предостережений:
-
Рекурсивные объекты не будут работать. Например:
var o = { a: "foo" };
o.b = o;
flatten(o);
будет возвращаться, пока не выдает исключение.
-
Как и запрос ruquay, это вытаскивает элементы массива так же, как и обычные свойства объекта. Если вы хотите сохранить массивы без изменений, добавьте "|| prop instanceof Array
" к исключениям.
-
Если вы вызываете это на объекты из другого окна или фрейма, даты и регулярные выражения не будут включены, так как instanceof
не будет работать должным образом. Вы можете исправить это, заменив его стандартным методом toString следующим образом:
Object.prototype.toString.call(prop) === "[object Date]"
Object.prototype.toString.call(prop) === "[object RegExp]"
Object.prototype.toString.call(prop) === "[object Array]"
Ответ 2
Здесь быстрый, но смотрите, b/c он не будет работать с массивами и нулевыми значениями (b/c их тип возвращает "объект" ).
var flatten = function(obj, prefix) {
if(typeof prefix === "undefined") {
prefix = "";
}
var copy = {};
for (var p in obj) {
if(obj.hasOwnProperty(p)) {
if(typeof obj[p] === "object") {
var tmp = flatten(obj[p], p + "_");
for(var q in tmp) {
if(tmp.hasOwnProperty(q)) {
copy[prefix + q] = tmp[q];
}
}
}
else {
copy[prefix + p] = obj[p];
}
}
}
return copy;
}
var myObj = {
a: "level 1",
b: {
a: "level 2",
b: {
a: "level 3",
b: "level 3"
}
}
}
var flattened = flatten(myObj);
Ответ 3
Вот быстрая версия CoffeeScript, основанная на ответе Мэтью Крамли (я didn 't использовать includePrototype
, поскольку я не нуждался в этом):
flatten = (obj, into = {}, prefix = '', sep = '_') ->
for own key, prop of obj
if typeof prop is 'object' and prop not instanceof Date and prop not instanceof RegExp
flatten prop, into, prefix + key + sep, sep
else
into[prefix + key] = prop
into
И базовая unflatten версия, которая, несомненно, потерпит неудачу с повторными разделителями и другой такой хитростью:
unflatten = (obj, into = {}, sep = '_') ->
for own key, prop of obj
subKeys = key.split sep
sub = into
sub = (sub[subKey] or= {}) for subKey in subKeys[...-1]
sub[subKeys.pop()] = prop
into
FWIW, я использую эти функции, чтобы нажимать графы объектов в Redis хэши, которые поддерживают только одну глубину пар ключ/значение.