javascript - 使用 javascript 循环遍历 json-Array
问题描述
我在 nodejs 中使用 javascript 循环遍历 json-Array 时遇到问题。我的名为birthdaysArray 的数组看起来像:
[{"birth":"23.04.1988","name":"Tom Smith"},{"birth":"15.04.2010","name":"Mini Jall"},...,{"birth":"23.04.2001","name":"Michael Bird"},{"birth":"15.11.1999","name":"Frank Middleton"}]
现在我想要一个列出当月生日(按出生日期排序)的数组。具有相同日期和月份(例如 23.04)的条目应在相同日期列出。它应该看起来像,如果4 月是当前月份(列表有 100 多个条目。):
[{"day":"15","name":["Mini Jall"]},{"day":"23", "name": ["Tom Smith","Michael Bird"]}]
我环顾四周,但我没有找到解决方案。我检查了这个:
for(var i = 0; i < json.length; i++) {
var obj = json[i];
console.log(obj.id);
}
但不匹配。谁能帮忙?
附件1:我在node_helper.js中的代码:
start() {
console.log("Starting module helper: " + this.name);
// console.log("Pfad zur csv-Datei: " + this.path + "/data/birthdays.csv");
const csvFilePath = this.path + '/data/birthdays.csv';
csv()
.fromFile(csvFilePath)
.then((jsonObj)=>{
birthdaysArray = JSON.stringify(jsonObj);
console.log("birthdaysArray: " + birthdaysArray);
var result = Object.entries(birthdaysArray.reduce((a, {birth, name}) => {
const day = +birth.split('.')[0];
a[day] = [...(a[day] || []), name];
return a
}, {})).map(([day, name]) => ({day, name})).sort((a, b) => +a.day - b.day)
console.log("sorted birthdays : " + result);
})
}
=> 输出 console.log :
Unhandled rejection TypeError: birthdaysArray.reduce is not a function
at /home/dirk/MagicMirror/modules/perlchamp/node_helper.js:27:47
at Object.onfulfilled (/home/dirk/MagicMirror/node_modules/csvtojson/v2/Converter.js:112:33)
at Result.endProcess (/home/dirk/MagicMirror/node_modules/csvtojson/v2/Result.js:83:50)
at Converter.processEnd (/home/dirk/MagicMirror/node_modules/csvtojson/v2/Converter.js:179:21)
at /home/dirk/MagicMirror/node_modules/csvtojson/v2/Converter.js:172:19
at tryCatcher (/home/dirk/MagicMirror/node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (/home/dirk/MagicMirror/node_modules/bluebird/js/release/promise.js:547:31)
at Promise._settlePromise (/home/dirk/MagicMirror/node_modules/bluebird/js/release/promise.js:604:18)
at Promise._settlePromise0 (/home/dirk/MagicMirror/node_modules/bluebird/js/release/promise.js:649:10)
at Promise._settlePromises (/home/dirk/MagicMirror/node_modules/bluebird/js/release/promise.js:729:18)
at _drainQueueStep (/home/dirk/MagicMirror/node_modules/bluebird/js/release/async.js:93:12)
at _drainQueue (/home/dirk/MagicMirror/node_modules/bluebird/js/release/async.js:86:9)
at Async._drainQueues (/home/dirk/MagicMirror/node_modules/bluebird/js/release/async.js:102:5)
at Immediate.Async.drainQueues [as _onImmediate] (/home/dirk/MagicMirror/node_modules/bluebird/js/release/async.js:15:14)
at processImmediate (internal/timers.js:439:21)
那么,我该怎么办?
附件2:首先我想按月过滤字符串:
var today_month = moment().format("MM")
var monthList = [];
for(let prop in jsonObj) {
if (jsonObj[prop]["birth"].split(".")[1] == today_month) {
//console.log("fifth: ", jsonObj[prop]);
monthList += jsonObj[prop];
}
}
console.log("monthList: ", monthList);
...然后将代码(绿色钩子,第二个)应用到它。不幸的是,它不像我想象的那样工作,你现在可以一次完成吗?因此,按当月从年度生日列表中过滤,然后按我上面提到的那样显示这些条目。
解决方案
目标是创建一个Object的数组,其结构是。请注意,重复的日子将被分组到同一个对象中,谁的日子表示分组。[{ day: String, name: [String] }, …]
输入数据是一个数组,其格式为[{ birth: String, name: String }, …]
.
首先,您需要从没有以标准方式(例如,)格式化的日期字符串中提取有意义的日期
"25.04.1988"
。
要转换此字符串,您可以使用带有回调参数的正则表达式,该参数以ISO-8601 日期格式对日期进行排序,ECMA标准规定Date
在其构造中必须支持该格式。另请注意,Date期望其输入为 UTC 时间,即它不知道时区。
这样的结构可能看起来像
const input = new Date('23.04.1988'.replace(/^(\d+)[^\d+](\d+)[^\d+](\d+)$/, (...backRefs) => {
return `${backRefs[3]}-${backRefs[2]}-${backRefs[1]}`;
}));
这里使用 ECMAScript 语法(Node.js实现的)来声明正则表达式文字:/RegularExpressionBody/RegularExpressionFlags
,其中标志为空,表达式主体为^(\d+)[^\d+](\d+)[^\d+](\d+)$
.
此正则表达式与有效日期不匹配,而是由三个数字系列的任何构造匹配,由构成整个String(\d+)
的非数字字符打破。然后,它使用反向引用按照ISO-8601 Date的格式将它们按破折号分隔的顺序重建它们。[^\d+]
3-2-1
那是使用模板文字`${backRefs[3]}-${backRefs[2]}-${backRefs[1]}`
的部分,通常被错误地称为模板字符串。
从这个新变量构造一个日期 对象const input
是可行的,但如果它确实是无效的,它的toString()
方法将返回字符串。 "Invalid Date"
如果输入日期与正则表达式不匹配,则反向引用索引未捕获的捕获组;那么构造的Date的类型是因为它将使用无效输入构造。此特定结果可用于汇总无效的出生日期。
undefined
现在您知道如何正确提取日期,您可以对输入进行排序和分组。
const input = [{
"birth" : "23.04.1988",
"name": "Tom Smith"
},
{
"birth": "15.04.2010",
"name": "Mini Jall"
},
{
"birth": "23.04.2001",
"name":"Michael Bird"
}];
让input
成为您的输入Array of Object。
Array 对象提供了一个在其原型上调用的函数,您可以使用它,因为它是一个Array。这个函数的规范是。它为Array中存在的每个元素调用一次,该元素是被遍历的对象。回调的返回值替换完成时返回的临时数组中的原始对象。换句话说,您可以映射您的Arraymap
input
Array.prototype.map(callbackFn[, thisArg])
map
进入一个新结构,方法是在迭代时使用每个元素的属性从回调中返回该结构。请注意,参数是调用函数thisArg
的上下文,如果调用,则上下文将被继承,因此,只是可选的。map
input.map
input
thisArg
这样的调用看起来像argumentsListinput.map(argumentsList)
是一个仅包含回调函数的列表。回调最多需要三个参数:currentValue、currentIndex 和被遍历的对象。
所以你的回调应该采取以下形式
(curr, idx, arr) => { return…; } // or
function (curr, idx, arr) { return…; }
在此回调中,您希望将birth
参数转换为day,因此使用所讨论的方法,您可以执行类似的操作
let dateCB = ({ birth, name }, idx, arr) => {
const dateString = birth.replace(/^(\d+)[^\d+](\d+)[^\d+](\d+)$/, (...backRefs) => {
return `${backRefs[3]}-${backRefs[2]}-${backRefs[1]}`;
});
const date = new Date(dateString);
const retObj = { day: date.getDate(), month: date.getUTCMonth() + 1, name };
Object.defineProperty(retObj, 'month', { enumerable: false });
return retObj;
};
我们添加1
到月份是因为getUTCMonth
返回一年中索引为零的月份。此外,我们将属性定义month
为不可枚举,因为我们不希望它出现在结果对象中。另请注意,curr
从前面开始,正在解构{ birth, name }
并在{ day: date.getDate(), month: date.getUTCMonth() + 1, name }
. 解构赋值允许您将Object的属性简化为属性名称,并在argumentsList中将这些属性声明为箭头函数范围内的变量。这实际上是一个简写,{ birth: birth, name: name }
因为它们与输入对象的属性具有相同的标识符curr
。
此时,您将拥有一个Object数组。
[
{
"day" :23,
"month": 4,
"name": "Tom Smith"
},
{
"day": 15,
"month": 4,
"name": "Mini Jall"
},
{
"day": 23,
"month": 4,
"name": "Michael Bird"
}
]
除了month
属性将不可枚举。
您希望整理这些Object,以便String 成为所有s 中的一个,其父name
Object与Array的其他Object成员共享相同的属性和属性。Array
name
day
month
因此,我们将考虑使用Array.prototype.reduce
定义为Array.prototype.reduce(callbackfn[, initialValue])
. 回调函数最多接受四个参数:previousValue、currentValue、currentIndex 和被遍历的对象。如果您提供initialValue
thenreduce
从第一个元素开始迭代,并将initialValue
提供给回调作为previousValue
. 但是,如果您省略initialValue
thenreduce
从第二个元素开始迭代,将第一个元素提供为previousValue
. 然后从回调返回的内容作为它的previousValue
.
我们还将使用Array.prototype.findIndex
定义为Array.prototype.findIndex(predicate[, thisArg ])
. 谓词Function最多需要三个参数,并且应该返回一个布尔强制结果(例如1
, 0
, true
, false
, undefined
, 等)。如果没有谓词返回,findIndex
则返回,或者返回第一个谓词返回时到达的索引。-1
true
我们可以使用它来确定数组是否包含匹配项day
以及month
reducer 的迭代。
({ day: tDay, month: tMonth, name: tName }) => {
return tDay === … && tMonth === …;
}
我们想要构造一个新的Array输出,我们将使用 迭代输入reduce
,同时构造输出。出于这个原因,reduce 将使用一个空数组作为其可选的第二个参数来调用
let reducer = (prev, { day, month, name }) => {
if (prev.length === 0) { /// this is the first iteration, where prev is empty
prev.push({ day, month, name: [name] });
} else { /// this is any other iteration, now we have to search `prev`
let where = prev.findIndex(({ day: tDay, month: tMonth, name: tName }) => {
return tDay === day && tMonth === month;
});
if (where !== -1) {
prev[where].name
.push(name);
} else {
prev.push({ day, month, name: [name] });
}
}
return prev;
}
调用看起来像
input.map(dateCB)
.reduce(reducer, []);
最后我们看看
Array.prototype.sort
定义为的函数Array.prototype.sort(comparefn)
。该comparefn
函数接收两个参数,x
和y
。的工作comparefn
是描述如何x
与y
. 排序器期望从if 、零 if和正 if返回一个负数。和是被排序的数组的两个成员,因此由于这些成员具有相同的结构,您可以解构并返回一个足够的结果。
comparefn
x < y
x == y
x > y
x
y
x
y
{ day: xDay }, { day: yDay }
xDay - yDay
在最后一个片段中,我引入了一个新变量
cDay
,它只是属性的字符串表示形式day
。我还在Array中的最终Object上实现了不可枚举的属性,并在输出上实现了排序器。
const input = [{
"birth" : "23.04.1988",
"name": "Tom Smith"
},
{
"birth": "15.04.2010",
"name": "Mini Jall"
},
{
"birth": "23.04.2001",
"name":"Michael Bird"
}];
const dateCB = ({ birth, name }, idx, arr) => {
const dateString = birth.replace(/^(\d+)[^\d+](\d+)[^\d+](\d+)$/, (...backRefs) => {
return `${backRefs[3]}-${backRefs[2]}-${backRefs[1]}`;
});
const date = new Date(dateString);
const retObj = { day: date.getDate(), month: date.getUTCMonth() + 1, name };
return retObj;
};
const reducer = (prev, { day, month, name }) => {
const cDay = day.toString(10);
const retObj = { day: cDay, month, name: [name] };
Object.defineProperty(retObj, 'month', { enumerable: false });
if (prev.length === 0) {
prev.push(retObj);
} else {
const where = prev.findIndex(({ day: tDay, month: tMonth, name: tName }) => {
return tDay === cDay && tMonth === month;
});
if (where !== -1) {
prev[where].name
.push(name);
} else {
prev.push(retObj);
}
}
return prev;
};
const sorter = ({ day: bDay }, { day: aDay }) => {
return bDay - aDay;
};
const output = input.map(dateCB).reduce(reducer, []).sort(sorter);
console.log(JSON.stringify(output));
推荐阅读
- javascript - 获取类属性列表
- powershell - Remote install MSI package not working when logged off
- google-app-engine - 组合 cron 作业以减少实例数量
- angular - 测试 Angular 类
- xml - 使用 XML XSLT,如何更新 XML 中的特定数据?
- ruby - 捆绑安装:'minitest/autorun x64-mingw32' 不在 Gemfile 中
- swagger - 调试 Swashbuckle 错误 - 无法加载 API 定义
- angular - MatTable OR 过滤器
- mysql - 重启后 MySQL 凭据被拒绝
- unity3d - 2d 游戏中的画布大小混淆