gpt4 book ai didi

json - Node JS : Make a flat json from a tree json

转载 作者:搜寻专家 更新时间:2023-10-31 23:04:22 24 4
gpt4 key购买 nike

我正在编写一个 node.js 脚本,将所有 json 文件合并到一个目录中,并将结果存储为一个新的 json 文件。我尝试在很大程度上完成这项工作,但几乎没有缺陷。

A.json

[
{
"id": "addEmoticon1",
"description": "Message to greet the user.",
"defaultMessage": "Hello, {name}!"
},
{
"id": "addPhoto1",
"description": "How are youu.",
"defaultMessage": "How are you??"
}
]

B.json

[
{
"id": "close1",
"description": "Close it.",
"defaultMessage": "Close!"
}
]

我最终需要的是:

result.json

{
"addEmoticon1": "Hello, {name}!",
"addPhoto1": "How are you??",
"close1": "Close!"
}

我写了一个 node.js 脚本:

var fs = require('fs');

function readFiles(dirname, onFileContent, onError) {
fs.readdir(dirname, function(err, filenames) {
if (err) {
onError(err);
return;
}
filenames.forEach(function(filename) {
fs.readFile(dirname + filename, 'utf-8', function(err, content) {
if (err) {
onError(err);
return;
}
onFileContent(filename, content);
});
});
});
}

var data = {};
readFiles('C:/node/test/', function(filename, content) {
data[filename] = content;
var lines = content.split('\n');
lines.forEach(function(line) {
var parts = line.split('"');
if (parts[1] == 'id') {
fs.appendFile('result.json', parts[3]+': ', function (err) {});
}
if (parts[1] == 'defaultMessage') {
fs.appendFile('result.json', parts[3]+',\n', function (err) {});
}
});
}, function(err) {
throw err;
});

它提取“id”和“defaultMessage”但无法正确附加。

我得到的:

result.json

addEmoticon1: addPhoto1: Hello, {name}!,
close1: How are you??,
Close!,

每次运行我的脚本时,这个输出都是不同的。

  • 目标 1:用双引号将项目括起来,

  • 目标 2:在顶部和末尾添加花括号

  • 目标 3:最后一个元素末尾没有逗号

  • 目标 4:每次运行脚本时输出相同

最佳答案

我将从完成的解决方案开始......

这个答案的末尾有一个很大的解释。让我们先从大局考虑一下。

readdirp('.')
.fmap(filter(match(/\.json$/)))
.fmap(map(readfilep))
.fmap(map(fmap(JSON.parse)))
.fmap(concatp)
.fmap(flatten)
.fmap(reduce(createMap)({}))
.fmap(data=> JSON.stringify(data, null, '\t'))
.fmap(writefilep(resolve(__dirname, 'result.json')))
.then(filename=> console.log('wrote results to %s', filename), err=>console.error(err));

控制台输出

wrote results to /path/to/result.json

result.json(我添加了一个带有一些数据的 c.json 以表明这适用于 2 个以上的文件)

{
"addEmoticon1": "Hello, {name}!",
"addPhoto1": "How are you??",
"close1": "Close!",
"somethingelse": "Something!"
}

实现

我为 readdirreadFilewriteFile 创建了基于 Promise 的接口(interface)

import {readdir, readFile, writeFile} from 'fs';

const readdirp = dir=>
new Promise((pass,fail)=>
readdir(dir, (err, filenames) =>
err ? fail(err) : pass(mapResolve (dir) (filenames))));

const readfilep = path=>
new Promise((pass,fail)=>
readFile(path, 'utf8', (err,data)=>
err ? fail(err) : pass(data)));

const writefilep = path=> data=>
new Promise((pass,fail)=>
writeFile(path, data, err=>
err ? fail(err) : pass(path)));

为了将函数映射到我们的 Promise,我们需要一个 fmap 实用程序。请注意我们如何小心地将错误冒出来。

Promise.prototype.fmap = function fmap(f) {
return new Promise((pass,fail) =>
this.then(x=> pass(f(x)), fail));
};

这是其余的实用程序

const fmap = f=> x=> x.fmap(f);
const mapResolve = dir=> map(x=>resolve(dir,x));
const map = f=> xs=> xs.map(x=> f(x));
const filter = f=> xs=> xs.filter(x=> f(x));
const match = re=> s=> re.test(s);
const concatp = xs=> Promise.all(xs);
const reduce = f=> y=> xs=> xs.reduce((y,x)=> f(y)(x), y);
const flatten = reduce(y=> x=> y.concat(Array.isArray(x) ? flatten (x) : x)) ([]);

最后,完成您的工作的一个自定义函数

const createMap = map=> ({id, defaultMessage})=>
Object.assign(map, {[id]: defaultMessage});

这里是 c.json

[
{
"id": "somethingelse",
"description": "something",
"defaultMessage": "Something!"
}
]

“为什么有这么多小功能?”

好吧,不管你怎么想,你有一个相当大的问题。大问题是通过组合几个小解决方案来解决的。这段代码最突出的优点是每个函数都有非常不同的目的,并且它总是对相同的输入产生相同的结果。这意味着每个函数都可以在程序的其他地方使用。另一个优点是较小的函数更易于阅读、推理和调试。

将所有这些与此处给出的其他答案进行比较;特别是@BlazeSahlen。那是 60 多行代码,基本上只能用于解决这个特定问题。而且它甚至不会过滤掉非 JSON 文件。因此,下次您需要创建一系列读取/写入文件的操作时,您每次都必须重写这 60 行中的大部分内容。由于样板文件耗尽,它会产生大量重复代码和难以发现的错误。还有所有那些手动错误处理……哇,现在杀了我吧。 他/她认为回调 hell 很糟糕?哈哈,他/她只是自己创造了另一个 hell 圈。


所有代码放在一起...

函数(大致)按使用顺序出现

import {readdir, readFile, writeFile} from 'fs';
import {resolve} from 'path';

// logp: Promise<Value> -> Void
const logp = p=> p.then(x=> console.log(x), x=> console.err(x));

// fmap : Promise<a> -> (a->b) -> Promise<b>
Promise.prototype.fmap = function fmap(f) {
return new Promise((pass,fail) =>
this.then(x=> pass(f(x)), fail));
};

// fmap : (a->b) -> F<a> -> F<b>
const fmap = f=> x=> x.fmap(f);

// readdirp : String -> Promise<Array<String>>
const readdirp = dir=>
new Promise((pass,fail)=>
readdir(dir, (err, filenames) =>
err ? fail(err) : pass(mapResolve (dir) (filenames))));

// mapResolve : String -> Array<String> -> Array<String>
const mapResolve = dir=> map(x=>resolve(dir,x));

// map : (a->b) -> Array<a> -> Array<b>
const map = f=> xs=> xs.map(x=> f(x));

// filter : (Value -> Boolean) -> Array<Value> -> Array<Value>
const filter = f=> xs=> xs.filter(x=> f(x));

// match : RegExp -> String -> Boolean
const match = re=> s=> re.test(s);

// readfilep : String -> Promise<String>
const readfilep = path=>
new Promise((pass,fail)=>
readFile(path, 'utf8', (err,data)=>
err ? fail(err) : pass(data)));

// concatp : Array<Promise<Value>> -> Array<Value>
const concatp = xs=> Promise.all(xs);

// reduce : (b->a->b) -> b -> Array<a> -> b
const reduce = f=> y=> xs=> xs.reduce((y,x)=> f(y)(x), y);

// flatten : Array<Array<Value>> -> Array<Value>
const flatten = reduce(y=> x=> y.concat(Array.isArray(x) ? flatten (x) : x)) ([]);

// writefilep : String -> Value -> Promise<String>
const writefilep = path=> data=>
new Promise((pass,fail)=>
writeFile(path, data, err=>
err ? fail(err) : pass(path)));

// -----------------------------------------------------------------------------

// createMap : Object -> Object -> Object
const createMap = map=> ({id, defaultMessage})=>
Object.assign(map, {[id]: defaultMessage});

// do it !
readdirp('.')
.fmap(filter(match(/\.json$/)))
.fmap(map(readfilep))
.fmap(map(fmap(JSON.parse)))
.fmap(concatp)
.fmap(flatten)
.fmap(reduce(createMap)({}))
.fmap(data=> JSON.stringify(data, null, '\t'))
.fmap(writefilep(resolve(__dirname, 'result.json')))
.then(filename=> console.log('wrote results to %s', filename), err=>console.error(err));

仍然无法跟进?

一开始要了解这些东西是如何工作的并不容易。这是一个特别棘手的问题,因为数据嵌套得非常快。值得庆幸的是,这并不意味着我们的代码必须是一个大的嵌套困惑才能解决问题!请注意,即使我们处理诸如 JSON 的 Promise 数组的 Promise 之类的事情,代码仍然保持良好和平坦......

// Here we are reading directory '.'
// We will get a Promise<Array<String>>
// Let's say the files are 'a.json', 'b.json', 'c.json', and 'run.js'
// Promise will look like this:
// Promise<['a.json', 'b.json', 'c.json', 'run.js']>
readdirp('.')

// Now we're going to strip out any non-JSON files
// Promise<['a.json', 'b.json', 'c.json']>
.fmap(filter(match(/\.json$/)))

// call `readfilep` on each of the files
// We will get <Promise<Array<Promise<JSON>>>>
// Don't freak out, it's not that bad!
// Promise<[Promise<JSON>, Promise<JSON>. Promise<JSON>]>
.fmap(map(readfilep))

// for each file's Promise, we want to parse the data as JSON
// JSON.parse returns an object, so the structure will be the same
// except JSON will be an object!
// Promise<[Promise<Object>, Promise<Object>, Promise<Object>]>
.fmap(map(fmap(JSON.parse)))

// Now we can start collapsing some of the structure
// `concatp` will convert Array<Promise<Value>> to Array<Value>
// We will get
// Promise<[Object, Object, Object]>
// Remember, we have 3 Objects; one for each parsed JSON file
.fmap(concatp)

// Your particular JSON structures are Arrays, which are also Objects
// so that means `concatp` will actually return Promise<[Array, Array, Array]
// but we'd like to flatten that
// that way each parsed JSON file gets mushed into a single data set
// after flatten, we will have
// Promise<Array<Object>>
.fmap(flatten)

// Here's where it all comes together
// now that we have a single Promise of an Array containing all of your objects ...
// We can simply reduce the array and create the mapping of key:values that you wish
// `createMap` is custom tailored for the mapping you need
// we initialize the `reduce` with an empty object, {}
// after it runs, we will have Promise<Object>
// where Object is your result
.fmap(reduce(createMap)({}))

// It's all downhill from here
// We currently have Promise<Object>
// but before we write that to a file, we need to convert it to JSON
// JSON.stringify(data, null, '\t') will pretty print the JSON using tab to indent
// After this, we will have Promise<JSON>
.fmap(data=> JSON.stringify(data, null, '\t'))

// Now that we have a JSON, we can easily write this to a file
// We'll use `writefilep` to write the result to `result.json` in the current working directory
// I wrote `writefilep` to pass the filename on success
// so when this finishes, we will have
// Promise<Path>
// You could have it return Promise<Void> like writeFile sends void to the callback. up to you.
.fmap(writefilep(resolve(__dirname, 'result.json')))

// the grand finale
// alert the user that everything is done (or if an error occurred)
// Remember `.then` is like a fork in the road:
// the code will go to the left function on success, and the right on failure
// Here, we're using a generic function to say we wrote the file out
// If a failure happens, we write that to console.error
.then(filename=> console.log('wrote results to %s', filename), err=>console.error(err));

全部完成!

关于json - Node JS : Make a flat json from a tree json,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37315616/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com