2016-06-20 4 views
2

У меня есть следующие JsonJavascript, как вернуть массив из карты

var myjson = [{ 

      "files": [ 
       { 
        "domain": "d", 
        "units": [ 
         { 
          "key": "key1", 
          "type": "2" 

        }, 
         { 
          "key": "key2", 
          "type": "2" 
        }, 
         { 
          "key": "key3", 
          "type": "2" 
        }] 

      }, 

       { 
        "domain": "d1", 
        "units": [ 
         { 
          "key": "key11", 
          "type": "2" 

        }, 
         { 
          "key": "key12", 
          "type": "2" 
        }, 
         { 
          "key": "key13", 
          "type": "2" 
        }] 

      }] 

      }, 

     { 

      "files": [ 
       { 
        "domain": "d", 
        "units": [ 
         { 
    ...... 

Я хочу, чтобы создать новый массив из этого массива JSON. Длина массива будет числом «единиц» в этом объекте Json.

Поэтому мне нужно извлечь «единицы» и добавить некоторые данные из родительских объектов.

units: [{ 
     domain: "", 
     type: "", 
     key: "" 
    }, { 
     domain: "", 
     type: "", 
     key: "" 
    }, 
    { 
     domain: "", 
     type: "", 
     key: "" 
    } 
.... 
    ]; 

Я думаю, я, вероятно, может сделать что-то вроде этого:

var res = []; 

myjson.forEach(function(row) { 
    row.files.forEach(function(tfile) { 
     tfile.units.forEach(function(unit) { 

      var testEntity = { 
       domain: tfile.domain, 
       type : unit.type, 
       key: unit.key 

      }; 

      res.push(testEntity); 

     }); 
    }); 
}); 

Но это трудно читать, и выглядит не так хорошо. Я думал сделать что-то вроде:

var RESULT = myjson.map(function(row) { 
    return row.files.map(function(tfile) { 
     return tfile.units.map(function(unit) { 

      return { 
       domain: tfile.domain, 
       type : unit.type, 
       key: unit.key 

      }; 



     }); 
    }); 
}); 

Но это не работает и выглядит не лучше. Есть ли способ сделать это, это работает, может быть, более декларативно. надеялся, что Ramda.js может помочь.

Есть ли какой-нибудь хороший подход вообще, чтобы получить данные из любого вложенного json в удобочитаемом виде?

Реализации что-то вроде:

nestedjson.findAllOnLastlevel(function(item){ 

return { 
key : item.key, 
type: type.key, 
domain : item.parent.domain} 

}); 

Или как-то сгладить это так все JSON свойства от всех родителей объекта перемещается в Лифс детей. myjson.flatten ("files.units")

jsbin http://jsbin.com/hiqatutino/edit?css,js,console

Большое спасибо

+0

поэтому вам нужен рабочий вариант, который у вас уже есть или хорошо выглядящий вариант, который может быть выполнен с помощью стиля ES6? –

+0

теперь у меня рабочая уродливая версия, но я делаю много таких «вложенных операций json». Думал о правильном подходе. Возможно, даже какой-то общий подход. Предоставленный json I упрощен. Это может быть глубже. Кроме того, новый «=>» недоступен в моем случае. – Ant

+0

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

ответ

5

Функция Вы можете использовать здесь является R.chain функция Ramda, а не R.map. Вы можете думать о R.chain как способ сопоставления списка с функцией, которая возвращает другой список, а затем сглаживает результирующий список списков.

// get a list of all files 
const listOfFiles = 
    R.chain(R.prop('files'), myjson) 

// a function that we can use to add the domain to each unit 
const unitsWithDomain = 
    (domain, units) => R.map(R.assoc('domain', domain), units) 

// take the list of files and add the domain to each of its units 
const result = 
    R.chain(file => unitsWithDomain(file.domain, file.units), listOfFiles) 

Если вы хотите взять его на шаг дальше, то вы можете также использовать R.pipeK, который помогает с слагающих функций вместе, которые ведут себя как R.chain между каждым из заданных функций.

// this creates a function that accepts the `myjson` list 
// then passes the list of files to the second function 
// returning the list of units for each file with the domain attached 
const process = pipeK(prop('files'), 
         f => map(assoc('domain', f.domain), f.units)) 

// giving the `myjson` object produces the same result as above 
process(myjson) 
+0

выглядит хорошо - я упростил свой json, если у меня есть несколько (пять) свойств, которые нужно быть ассоциированным в том же месте, что и домен? И еще несколько свойств на первом уровне prop1: "value," files ": {.... Я добавил здесь http://jsbin.com/samilit/edit?css,js,console – Ant

+0

' var result = R.pipe ( R.chain (функция (p) {return R.map (R.assoc ('parent', p), p.files)}, R.chain (функция (p) {return R.map (R .assoc ('parent', p), p.units)}), R.chain (функция (k) {return { ) домен: k.parent.domain, prop1: k.parent.parent.prop1, key: k.key}}) ) (myjson) ' – Ant

+0

Да, это потребует потоковой передачи значений свойств как состояния через каждый шаг, например http://jsbin.com/vepumikuve/1/edit?js,console –

1

Что-то, как это должно работать. .reduce является хорошим для подобных ситуаций.

const allUnits = myjson.reduce((acc, anonObj) => { 
    const units = anonObj.files.map(fileObj => { 
    return fileObj.units.map(unit => { 
     return {...unit, domain: fileObj.domain}) 
    }) 
    return [...acc, ...units] 
}, []) 

Обратите внимание, что это зависит как массив распространения и объект расширения, которые ES6, не поддерживаемые на каждой платформе.

Если вы не можете использовать ES6, вот реализация ES5. Не так красиво, но делает то же самое:

var allUnits = myjson.reduce(function (acc, anonObj) { 
    const units = anonObj.files.map(function(fileObj) { 
    // for each fileObject, return an array of processed unit objects 
    // with domain property added from fileObj 
    return fileObj.units.map(function(unit) { 
     return { 
     key: unit.key, 
     type: unit.type, 
     domain: fileObj.domain 
     } 
    }) 
    }) 
    // for each file array, add unit objects from that array to accumulator array 
    return acc.concat(units) 
}, []) 
+0

У меня есть ES5, и нет способа его изменить :( – Ant

0

Попробуйте

var myjson = [{ 
 

 
"files": [{ 
 
    "domain": "d", 
 
    "units": [{ 
 
     "key": "key1", 
 
     "type": "2" 
 
    }, { 
 
     "key": "key2", 
 
     "type": "2" 
 
    }, { 
 
     "key": "key3", 
 
     "type": "2" 
 
    }] 
 
    }, 
 
    { 
 
    "domain": "d1", 
 
    "units": [{ 
 
     "key": "key11", 
 
     "type": "2" 
 

 
    }, { 
 
     "key": "key12", 
 
     "type": "2" 
 
    }, { 
 
     "key": "key13", 
 
     "type": "2" 
 
    }] 
 
    } 
 
] 
 
}]; 
 

 
//first filter out properties exluding units 
 
var result = []; 
 
myjson.forEach(function(obj){ 
 
    obj.files.forEach(function(obj2){ 
 
    result = result.concat(obj2.units.map(function(unit){ 
 
     unit.domain = obj2.domain; 
 
     return unit; 
 
    })); 
 
    }); 
 
}); 
 

 
console.log(result);

+0

работает, может ли что-то, что не требует var result = []; может быть сделано? В неизменном виде? Map всегда возвращает один результат? Может ли он вернуть «вид нескольких», – Ant

+0

Ошибка 'domain' отсутствует – RomanPerekhrest

+0

@ Карта на всех трех уровнях вернет вам что-то вроде' [[[{key: 'key1', value: '1'}]]] 'и для этого вам нужно написать код чтобы принимать отдельные элементы и позже конкатенировать их с массивом. Поскольку вам нужен более короткий код, я не могу получить его короче, чем это :) – gurvinder372

2

Pure JS очень подходит для получения результата в простых одном вкладышах. Я бы не касался какой-либо библиотеки только для этой работы. У меня есть два способа сделать это здесь. Первая - цепочка reduce.reduce.map, а вторая - цепочка reduce.map.map. Вот код;

var myjson = [{"files":[{"domain":"d","units":[{"key":"key1","type":"2"},{"key":"key2","type":"2"},{"key":"key3","type":"2"}]},{"domain":"d1","units":[{"key":"key11","type":"2"},{"key":"key12","type":"2"},{"key":"key13","type":"2"}]}]},{"files":[{"domain":"e","units":[{"key":"key1","type":"2"},{"key":"key2","type":"2"},{"key":"key3","type":"2"}]},{"domain":"e1","units":[{"key":"key11","type":"2"},{"key":"key12","type":"2"},{"key":"key13","type":"2"}]}]}], 
 
    units = myjson.reduce((p,c) => c.files.reduce((f,s) => f.concat(s.units.map(e => (e.domain = s.domain,e))) ,p) ,[]); 
 
    units2 = myjson.reduce((p,c) => p.concat(...c.files.map(f => f.units.map(e => (e.domain = f.domain,e)))) ,[]); 
 
    console.log(units); 
 
    console.log(units2);

Для совместимости ES5 я хотел бы предложить reduce.reduce.map цепь, так как нет необходимости для оператора распространения. И замените функции стрелок на их обычные аналоги, как показано ниже;

var myjson = [{"files":[{"domain":"d","units":[{"key":"key1","type":"2"},{"key":"key2","type":"2"},{"key":"key3","type":"2"}]},{"domain":"d1","units":[{"key":"key11","type":"2"},{"key":"key12","type":"2"},{"key":"key13","type":"2"}]}]},{"files":[{"domain":"e","units":[{"key":"key1","type":"2"},{"key":"key2","type":"2"},{"key":"key3","type":"2"}]},{"domain":"e1","units":[{"key":"key11","type":"2"},{"key":"key12","type":"2"},{"key":"key13","type":"2"}]}]}], 
 
    units = myjson.reduce(function(p,c) { 
 
     \t      return c.files.reduce(function(f,s) { 
 
     \t      \t      return f.concat(s.units.map(function(e){ 
 
     \t      \t      \t       e.domain = s.domain; 
 
     \t      \t      \t       return e; 
 
     \t      \t      \t       })); 
 
     \t           },p); 
 
     \t     },[]); 
 
console.log(units);