首页 > 解决方案 > 使用 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);

...然后将代码(绿色钩子,第二个)应用到它。不幸的是,它不像我想象的那样工作,你现在可以一次完成吗?因此,按当月从年度生日列表中过滤,然后按我上面提到的那样显示这些条目。

标签: javascriptarraysjsonloopssorting

解决方案


目标是创建一个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中存在的每个元素调用一次,该元素是被遍历的对象。回调的返回值替换完成时返回的临时数组中的原始对象。换句话说,您可以映射您的ArraymapinputArray.prototype.map(callbackFn[, thisArg]) map进入一个新结构,方法是在迭代时使用每个元素的属性从回调中返回该结构。请注意,参数是调用函数thisArg的上下文,如果调用,则上下文将被继承,因此,只是可选的。map input.mapinputthisArg

这样的调用看起来像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 中的一个,其父nameObjectArray的其他Object成员共享相同的属性和属性。Arraynamedaymonth

因此,我们将考虑使用Array.prototype.reduce定义为Array.prototype.reduce(callbackfn[, initialValue]). 回调函数最多接受四个参数:previousValue、currentValue、currentIndex 和被遍历的对象。如果您提供initialValuethenreduce从第一个元素开始迭代,并将initialValue提供给回调作为previousValue. 但是,如果您省略initialValuethenreduce从第二个元素开始迭代,将第一个元素提供为previousValue. 然后从回调返回的内容作为它的previousValue.

我们还将使用Array.prototype.findIndex定义为Array.prototype.findIndex(predicate[, thisArg ]). 谓词Function最多需要三个参数,并且应该返回一个布尔强制结果(例如1, 0, true, false, undefined, 等)。如果没有谓词返回,findIndex则返回,或者返回第一个谓词返回时到达的索引。-1true

我们可以使用它来确定数组是否包含匹配项day以及monthreducer 的迭代。

({ 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 函数接收两个参数,xy。的工作comparefn是描述如何xy. 排序器期望从if 、零 if和正 if返回一个负数。和是被排序的数组的两个成员,因此由于这些成员具有相同的结构,您可以解构并返回一个足够的结果。 comparefnx < yx == yx > yxyxy{ 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));


推荐阅读