遍历JSON以按键对值进行分组

时间:2019-02-22 04:27:58

标签: javascript

我有一个JSON文件,其中包含一堆歌曲以及相关的歌曲行,诗句编号等。我的目标是打印每个不同的诗句。我在构建有效的循环方面遇到困难。这是JSON文件的示例:

[{ "song": 1, "verse": 1, "lineNum": 1, "line": "Mato Joe Tsinki"},
 { "song": 1, "verse": 1, "lineNum": 2, "line": "Chiti chiti"},
 { "song": 1, "verse": 2, "lineNum": 1, "line": "Raro Raro"},
 { "song": 1, "verse": 2, "lineNum": 2, "line": "Shinan Kibi"},
 { "song": 1, "verse": 2, "lineNum": 3, "line": "Bewa bewa"},
 { "song": 2, "verse": 1, "lineNum": 1, "line": "Ramo Kano"},
 { "song": 2, "verse": 2, "lineNum": 1, "line": "Choro Choro"},
 { "song": 3, "verse": 1, "lineNum": 1, "line": "Pisha Pisha"}]

在此JSON中,共有5节经文。

  1. 歌曲1,第1节:“ Mato Joe Tsinki” /“ Chiti Chiti”
  2. 歌曲1,第2节:“ Raro Raro” /“ Shinan Kibi” /“ Bewa Bewa”
  3. 歌曲2,第1节:“ Ramo Kano”
  4. 歌曲2,第2节:“ Choro Choro”
  5. 歌曲3,第1节:“ Pisha Pisha”

我写了以下内容,可以成功地打印每首歌的第一节经文,但不能打印后续的经文。

var fs = require('fs');
var colors = require('colors');
fs.readFile('stackoverflow.json', 'utf8', function (err,data) {
data = JSON.parse(data); 

for(var i in data) { 
    var item = data[i];
    // construct string song:verse:line
    var lineIndex = item.song   
    lineIndex += ":"+item.verse
    lineIndex += ":"+item.lineNum

    var numSongs = 20
    var songNum = 1
    var verseNum = 1
    var lineNum = 1
    // console.log(verseNum)

        while(item.song <= numSongs && item.verse == verseNum)
        {       
            console.log(lineIndex.green + ' ' + item.line);
            verseNum++;
        }
}
});

已更新以演示控制台日志的期望输出:

歌曲1诗1: “马托·乔辛基” “ Chiti Chiti”

歌曲1诗2: “拉罗罗罗” “ Shinan Kibi” “ Bewa Bewa”

歌曲2诗1: ....

3 个答案:

答案 0 :(得分:1)

考虑使用Array.reduce(),而不是使用whilefor

lineslinelineNum组成的数组:

var arr = [{ "song": 1, "verse": 1, "lineNum": 1, "line": "Mato Joe Tsinki"}, { "song": 1, "verse": 1, "lineNum": 2, "line": "Chiti chiti"}, { "song": 1, "verse": 2, "lineNum": 1, "line": "Raro Raro"}, { "song": 1, "verse": 2, "lineNum": 2, "line": "Shinan Kibi"}, { "song": 1, "verse": 2, "lineNum": 3, "line": "Bewa bewa"}, { "song": 2, "verse": 1, "lineNum": 1, "line": "Ramo Kano"}, { "song": 2, "verse": 2, "lineNum": 1, "line": "Choro Choro"}, { "song": 3, "verse": 1, "lineNum": 1, "line": "Pisha Pisha"}];
 
let result = arr.reduce((out, i) => {
    let found = out.find(s => s.song === i.song && s.verse === i.verse);
    let {line, lineNum, ...newSong} = i;
    let {song, verse, ...newLine} = i;
    
    found 
      ? found.lines.push(newLine)
      : out.push({...newSong, lines: [newLine]});
      
    return out;
  }, []);
   
console.log(result);

以逗号分隔:

var arr = [{ "song": 1, "verse": 1, "lineNum": 1, "line": "Mato Joe Tsinki"}, { "song": 1, "verse": 1, "lineNum": 2, "line": "Chiti chiti"}, { "song": 1, "verse": 2, "lineNum": 1, "line": "Raro Raro"}, { "song": 1, "verse": 2, "lineNum": 2, "line": "Shinan Kibi"}, { "song": 1, "verse": 2, "lineNum": 3, "line": "Bewa bewa"}, { "song": 2, "verse": 1, "lineNum": 1, "line": "Ramo Kano"}, { "song": 2, "verse": 2, "lineNum": 1, "line": "Choro Choro"}, { "song": 3, "verse": 1, "lineNum": 1, "line": "Pisha Pisha"}];
 
let result = arr.reduce((out, i) => {
    var found = out.find(s => s.song === i.song && s.verse === i.verse);
    
    found 
      ? found.line += ", " + i.line  //Song found - combine the lines
      : out.push(i);                 //Song not found - add it as new item

    return out;
  }, []);
   
console.log(result);

reduce()将遍历数组中的每首歌曲以构建一个集合。如果已经在集合中找到相同的歌曲和诗句,则将这些行加在一起。如果没有,它将把整首歌曲添加到收藏中。

也就是说,您尚未明确定义期望输出的样子。

答案 1 :(得分:1)

您可以使用reduce对诗句进行分组,并使用songverse的{​​{1}}的每个唯一组合创建一个累加器对象。然后,使用Object.values获得最终输出。这将为keylines的每个组合创建一个song数组:

verse

如果您希望var verses = [{ "song": 1, "verse": 1, "lineNum": 1, "line": "Mato Joe Tsinki"}, { "song": 1, "verse": 1, "lineNum": 2, "line": "Chiti chiti"}, { "song": 1, "verse": 2, "lineNum": 1, "line": "Raro Raro"}, { "song": 1, "verse": 2, "lineNum": 2, "line": "Shinan Kibi"}, { "song": 1, "verse": 2, "lineNum": 3, "line": "Bewa bewa"}, { "song": 2, "verse": 1, "lineNum": 1, "line": "Ramo Kano"}, { "song": 2, "verse": 2, "lineNum": 1, "line": "Choro Choro"}, { "song": 3, "verse": 1, "lineNum": 1, "line": "Pisha Pisha"}] const merged = verses.reduce((acc, { song, verse, line }) => { const key = `${song}-${verse}` acc[key] = acc[key] || {song, verse, lines: []}; acc[key]["lines"].push(line); return acc }, {}) const output = Object.values(merged) console.log(output)lines分隔,请使用字符串属性并连接每个/

line

答案 2 :(得分:0)

这里有另一个版本,该版本使用第一个Array.forEach()将行按songverses进行分组,并注意将它们排在数组的首位(以防原始数组未订购)。然后,我们使用Array.map()获得您的预期输出:

let input = [
 { "song": 1, "verse": 1, "lineNum": 2, "line": "Chiti chiti"},
 { "song": 1, "verse": 1, "lineNum": 1, "line": "Mato Joe Tsinki"},
 { "song": 1, "verse": 2, "lineNum": 1, "line": "Raro Raro"},
 { "song": 1, "verse": 2, "lineNum": 3, "line": "Bewa bewa"},
 { "song": 1, "verse": 2, "lineNum": 2, "line": "Shinan Kibi"},
 { "song": 2, "verse": 1, "lineNum": 1, "line": "Ramo Kano"},
 { "song": 2, "verse": 2, "lineNum": 1, "line": "Choro Choro"},
 { "song": 3, "verse": 1, "lineNum": 1, "line": "Pisha Pisha"}
];

let verses = {};

input.forEach(o =>
{
    let k = `${o.song}_${o.verse}`;
    verses[k] = verses[k] || {song: o.song, verse: o.verse};
    verses[k]["lines"] = verses[k]["lines"] || [];
    verses[k]["lines"][o.lineNum] = o.line;
});

let res = Object.values(verses).map(
    o => `song ${o.song}, verse ${o.verse}, lines: ${o.lines.filter(Boolean).join(" / ")}`
);

console.log(res);