使用 ramda.js 将嵌套对象数组中的键值与标识符键匹配

Match values of a key in a nested array of objects to an identifier key using ramda.js

我正在尝试将这个嵌套对象数组和数组以及 return 该特定对象的标识符的总分得分放入一个新对象中。这是我正在使用的 JSON:

{
   "AllData" : [ {
        "company" : google,
        "featureData" : [{
           "ScoreTotal" : 10, 
           "featureName": 'test' 
           },{
           "ScoreTotal" : 10, 
           "featureName": 'test2' 
           },
           {
           "ScoreTotal" : 4, 
           "featureName": 'test3' 
           }]
        }, {
        "company" : amazon,
        "featureData" : [{
           "ScoreTotal" : 4, 
           "featureName": 'test' 
           },{
           "ScoreTotal" : 6, 
           "featureName": 'test2' 
           },
           {
           "ScoreTotal" : 3, 
           "featureName": 'test3' 
           }]
        },{
        "company" : facebook,
        "featureData" : [{
           "ScoreTotal" : 4, 
           "featureName": 'test' },
           {
           "ScoreTotal" : 6, 
           "featureName": 'test2' },
           {
           "ScoreTotal" : 2, 
           "featureName": 'test3' 
           }]
        }, 
   }]
}

我正在尝试创建一个对象数组,其中包含每个唯一 featureName 的得分总和以及相应的 featureName,如下所示:

[{featureName: 'test1', summedScore: '18'}, {featureName: 'test2', summedScore: '22'},{featureName: 'test3', summedScore: ' 9'}]

可以找到接近我要找的解决方案,但是解决方案中没有显示标识符与总和的匹配。

提前致谢!

我正在创建一个对象,我在其中使用特征的名称作为键,因此很容易创建他们的总分。然后我把这个对象变成符合你期望的数组。

const json = {
  "AllData": [{
      "company": 'google',
      "featureData": [{
          "ScoreTotal": 10,
          "featureName": 'test'
        }, {
          "ScoreTotal": 10,
          "featureName": 'test2'
        },
        {
          "ScoreTotal": 4,
          "featureName": 'test3'
        }
      ]
    }, {
      "company": 'amazon',
      "featureData": [{
          "ScoreTotal": 4,
          "featureName": 'test'
        }, {
          "ScoreTotal": 6,
          "featureName": 'test2'
        },
        {
          "ScoreTotal": 3,
          "featureName": 'test3'
        }
      ]
    }, {
      "company": 'facebook',
      "featureData": [{
          "ScoreTotal": 4,
          "featureName": 'test'
        },
        {
          "ScoreTotal": 6,
          "featureName": 'test2'
        },
        {
          "ScoreTotal": 2,
          "featureName": 'test3'
        }
      ]
  }]
};

const obj = json.AllData.reduce((tmp, x) => {
  x.featureData.forEach((y) => {
    tmp[y.featureName] = (tmp[y.featureName] || 0) + y.ScoreTotal;
  });

  return tmp;
}, {});

const arr = Object.keys(obj).map(x => ({
  summedScore: obj[x],
  featureName: x,
}));

console.log(arr);

这是 Ramda 的解决方案

const data = {
  "AllData": [{
    "company": "google",
    "featureData": [{
      "ScoreTotal": 10,
      "featureName": 'test'
    }, {
      "ScoreTotal": 10,
      "featureName": 'test2'
    },
    {
      "ScoreTotal": 4,
      "featureName": 'test3'
    }]
  }, {
    "company": "amazon",
    "featureData": [{
      "ScoreTotal": 4,
      "featureName": 'test'
    }, {
      "ScoreTotal": 6,
      "featureName": 'test2'
    },
    {
      "ScoreTotal": 3,
      "featureName": 'test3'
    }]
  }, {
    "company": "facebook",
    "featureData": [{
      "ScoreTotal": 4,
      "featureName": 'test'
    },
    {
      "ScoreTotal": 6,
      "featureName": 'test2'
    },
    {
      "ScoreTotal": 2,
      "featureName": 'test3'
    }]
  }
  ]
};

const getScores =
  R.pipe(
    // Group all featureData objects into a single array
    R.prop('AllData'),
    R.map(R.prop('featureData')),
    R.unnest,
    
    // Group all featureData objects with the same featureName into separate arrays
    R.groupBy(R.prop('featureName')),
    
    // Merge all objects in each array by summing their `ScoreTotal` properties
    R.map(R.reduce(R.mergeWithKey((key, left, right) => key === 'ScoreTotal' ? left + right : right), {})),
    R.values,
    
    // Reshape each object
    R.map(R.applySpec({
      featureName: R.prop('featureName'),
      summedScore: R.prop('ScoreTotal')
    })));

console.log(

  getScores(data)

);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>

我喜欢像这样一步一步地进行转换,从原始数据开始,一直到最终格式。我可以在 Ramda's REPL with a pipe 调用中执行此操作,逐个添加各个调用并检查结果是否朝着我想要的方向发展。

这样做,这就是我想出的答案。

const {pipe, prop, pluck, unnest, groupBy, map, sum, toPairs, zipObj} = R

const sumByFeatureName = pipe(
  prop('AllData'),
  pluck('featureData'),
  unnest,
  groupBy(prop('featureName')),
  map(pluck('ScoreTotal')),
  map(sum),
  toPairs,
  map(zipObj(['featureName', 'summedScore'])),
)

const json = {"AllData": [{"company": "google", "featureData": [{"ScoreTotal": 10, "featureName": "test"}, {"ScoreTotal": 10, "featureName": "test2"}, {"ScoreTotal": 4, "featureName": "test3"}]}, {"company": "amazon", "featureData": [{"ScoreTotal": 4, "featureName": "test"}, {"ScoreTotal": 6, "featureName": "test2"}, {"ScoreTotal": 3, "featureName": "test3"}]}, {"company": "facebook", "featureData": [{"ScoreTotal": 4, "featureName": "test"}, {"ScoreTotal": 6, "featureName": "test2"}, {"ScoreTotal": 2, "featureName": "test3"}]}]}

console.log(sumByFeatureName(json))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>

这演示了所有中间结果的结构:

const sumByFeatureName = pipe(
  prop('AllData'),                              //=> [{company: "google", featureData: [{ScoreTotal: 10, featureName: "test"}, ...], {company: 'amazon', ...}, ...]
  pluck('featureData'),                         //=> [[{ScoreTotal: 10, featureName: 'test"}, {ScoreTotal: 10, featureName: 'test2'}, ...] [...], ...]
  unnest,                                       //=> [{ScoreTotal: 10, featureName: 'test'}, {ScoreTotal: 10, featureName: 'test2'}, ...]  
  groupBy(prop('featureName')),                 //=> {test: [{ScoreTotal: 10, featureName: 'test'}, ...], test2: [{...}, ...]}
  map(pluck('ScoreTotal')),                     //=> {test: [10, 4, 4], test2: [10, 6, 6], test3: [4, 3, 2]} 
  map(sum),                                     //=> {test: 18, test2: 22, test3: 9}
  toPairs,                                      //=> [['test', 18], ['test2', 22], ['test3', 9]]
  map(zipObj(['featureName', 'summedScore'])),  //=> [{featureName: 'test, summedScore: 19}, ...] 

)

请注意,如果我们删除最后两行(toPairsmap(zipObj(...))),我们将得到以下格式:

{test: 18, test2: 22, test3: 9}

这通常是比您请求的输出更有用的结构。

另请注意,此类工作有助于确定我们自己的实用程序库中可能需要的有用功能,甚至建议将其添加到像 Ramda 这样的库中。如果我已经完成 toPairs/map(zipObj(...)) 洗牌足够多次,我可能会考虑为它编写自己的小函数,例如:

const objArray = (keyName, valName) => vals => 
  map(zipObj([keyName, valName]), toPairs(vals))

然后用 objArray('featureName', 'summedScore').

替换这两行