JavaScript如何从map中返回数组

3
我有以下的Json。
   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”的数量。
因此,我需要提取“units”并添加来自父对象的一些数据。
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

非常感谢


所以你需要一个可用的版本,这已经存在了,或者一个好看的版本,可以通过ES6样式实现? - Nina Scholz
现在我有一个能工作但是丑陋的版本,但我要做很多这种“嵌套的JSON操作”。我在思考一个适当的方法,甚至可能是一些通用的方法。我提供的JSON是简化的。它可能更深入。此外,我的情况下没有新的“=>”可用。 - Ant
主要问题是知道要迭代哪个属性。你可以使用递归方法给出路径,但至少需要重新组合属性。 - Nina Scholz
4个回答

5
在这里您可以使用Ramda的 R.chain 函数,而不是使用 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)

看起来不错 - 我简化了我的JSON,如果我有多个(五个)需要在同一个地方关联的属性,比如域名?还有一些其他的属性在第一层。prop1: "value","files": {....}我在这里添加了http://jsbin.com/samilit/edit?css,js,console - Ant
`var result = R.pipe(
R.chain(function(p) {return R.map(R.assoc('parent', p), p.files) }), R.chain(function(p) {return R.map(R.assoc('parent', p), p.units) }), R.chain(function(k) {return { domain : k.parent.domain, prop1 : k.parent.parent.prop1, key : k.key }}) ) (myjson)`
- Ant
是的,这需要通过每个步骤将属性值作为状态线程化。例如:http://jsbin.com/vepumikuve/1/edit?js,console - Scott Christopher

4

纯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) {
                           return c.files.reduce(function(f,s) {
                                                 return f.concat(s.units.map(function(e){
                                                                             e.domain = s.domain;
                                                                             return e;
                                                                           }));
                                                 },p);
                         },[]);
console.log(units);


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)
}, [])

我只有 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);


能否以不需要 var result = []; 的方式进行操作?是否可以使用 Immutable 的方式?map 方法总是返回一个结果吗?它能否返回“类似于多个”的结果? - Ant
缺少 domain 键。 - RomanPerekhrest
@Ant map 在所有三个级别上返回的结果可能是 [[[{key:'key1', value:'1'}]]],为了克服这个问题,您需要编写代码来接受单个项并将其后连接成一个数组。由于您想要更短的代码,我无法让它比这更短 :) - gurvinder372

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