【问题标题】:loop through a json-Array with javascript使用 javascript 循环遍历 json-Array
【发布时间】:2020-05-01 02:16:03
【问题描述】:

我在使用 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);

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

【问题讨论】:

  • 您的json 元素没有id,为什么您认为记录它会起作用?
  • 准确地说,这是一个对象数组——这里没有 JSON
  • 所以,您需要过滤数组中birth 属性在当前月份的对象,然后将该数组缩减为按出生日期对人进行分组,然后对结果进行排序数组以使其按顺序排列
  • 为什么(结果)"name": "Mini Jall" 而不是"name": ["Mini Jall"]
  • 你是对的,对不起。我会编辑它

标签: javascript arrays json loops sorting


【解决方案1】:

现在你改变了预期的结果 - 它更干净了一点

var data = [{
        "birth": "23.04.1988",
        "name": "Tom Smith"
    }, {
        "birth": "15.04.2010",
        "name": "Mini Jall"
    }, {
        "birth": "23.04.2001",
        "name": "Michael Bird"
    }, {
        "birth": "17.05.2001",
        "name": "May Gibbs"
    }, {
        "birth": "04.05.2001",
        "name": "May The Force be with you"
    }, {
        "birth": "17.05.2001",
        "name": "Jenny  May"
    }
];
var currentMonth = new Date().toLocaleDateString('en', {month:'2-digit'});
var result = Object.entries(data
    .filter(({birth}) => birth.split('.')[1] === currentMonth)
    .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(result);

【讨论】:

  • 抱歉代码不匹配。 => reduce 不是函数。
  • @perlchamp --- 运行 sn-p - 工作正常......不知道你在说什么
  • 这就是控制台打印的内容:[object Object],[object Object]
  • 给您的信息:我想为magicmirror 编写一个模块,代码在node_helper.js 中。也许我必须先将我的数组发送到我的 modulname.js ?我还不知道。
  • @perlchamp - 不,sn-p 中的控制台输出完全符合您的要求 - 如果您复制并更改了代码,那么这不会改变result 的事实这个答案,正是你想要的
【解决方案2】:

目标是创建一个 Array,其中包含 Object 的结构为 [{ day: String, name: [String] }, …]。注意重复的将被分组到同一个对象中,表示分组。

输入数据是一个数组,其格式为[{ birth: String, name: String }, …]


首先,您将希望从未以标准方式格式化的日期字符串(例如,"25.04.1988")中提取一个有意义的日期。

要转换此 String,您可以使用 正则表达式,并带有一个回调参数,该参数以 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+)$

此正则表达式与有效日期不匹配,而是由三组数字(\d+) 组成的任意构造,由非数字字符[^\d+] 分隔,构成整个字符串。然后,它使用按3-2-1 顺序的反向引用重构它们,并按照ISO-8601 日期 的格式用破折号分隔它们。

那是`${backRefs[3]}-${backRefs[2]}-${backRefs[1]}` 的部分,它利用了模板文字,通常被错误地称为模板字符串s。

从这个新变量 const input 构造一个 Date Object 会起作用,但它的 toString() 方法将返回 String @987654334 @如果确实如此,则无效。如果输入日期与正则表达式不匹配,则反向引用索引未捕获的捕获组;那么构造的 Datetypeundefined,因为它将使用无效输入构造。此特定结果可用于汇总无效的出生日期。


现在您知道如何正确提取日期,您可以对输入进行排序和分组。
const input = [{
    "birth" : "23.04.1988",
    "name": "Tom Smith"
},
{
    "birth": "15.04.2010",
    "name": "Mini Jall"
},
{
    "birth": "23.04.2001",
    "name":"Michael Bird"
}];

input 成为您输入的ObjectArray

Array Object 在其原型 上提供了一个名为map函数,您可以在@ 上使用它987654339@ 因为它是一个数组。这个函数的规范是Array.prototype.map(callbackFn[, thisArg])。对于 Array存在 的每个元素调用一次,该数组是被遍历的对象。回调的返回值替换 temporary Array 中的原始对象,该对象在完成时由map 返回。换句话说,您可以将 Array 映射到一个新结构中,方法是在迭代时使用每个元素的属性从回调中返回该结构。请注意,参数thisArg 是调用map Function 的上下文,如果您调用input.map,那么上下文将继承为input,因此thisArg只是可选的。

这样的调用类似于input.map(argumentsList),其中argumentsList 是一个仅包含回调Function 的列表。回调最多接受三个参数:currentValue、currentIndex 和被遍历的对象。

所以你的回调应该是

(curr, idx, arr) => { return…; } // or
function (curr, idx, arr) { return…; }

在此回调中,您希望将birth 参数转换为,因此使用所讨论的方法您可以执行类似

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 } 中重新构建。解构赋值允许您将 Objectproperties 简化为属性名称,并在 argumentsList 中将这些属性声明为范围内的变量箭头函数。这实际上是{ birth: birth, name: name } 的简写,因为它们具有与输入对象curr 的属性相同的标识符。


此时,您将拥有 ObjectArray
[
    {
        "day" :23,
        "month": 4,
        "name": "Tom Smith"
    },
    {
        "day": 15,
        "month": 4,
        "name": "Mini Jall"
    },
    {
        "day": 23,
        "month": 4,
        "name": "Michael Bird"
    }
]

除了month 属性不可枚举。

您希望整理这些 Object,使 String name 成为所有 names 的父 ObjectArray /strong> 与 Array 的其他 Object 成员共享相同的 daymonth 属性。

所以我们将考虑使用Array.prototype.reduce,它被定义为Array.prototype.reduce(callbackfn[, initialValue])。回调 Function 最多需要四个参数:previousValue、currentValue、currentIndex 和被遍历的对象。如果您提供initialValue,则reduce 从第一个元素开始迭代,并且initialValue 作为previousValue 提供给回调。但是,如果您省略 initialValue,则 reduce 从第二个元素开始迭代,提供第一个元素为 previousValue。您从回调中返回的内容将作为其previousValue 传递给下一次迭代。

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

我们可以使用它来确定数组是否包含匹配的 daymonth 以用于 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(comparefn)的函数Array.prototype.sortcomparefn 函数 接收两个参数,xycomparefn 的工作是描述 xy 的关系。如果x &lt; y,排序器期望从comparefn返回一个负Number,如果x == y返回零,如果x &gt; y返回正数。 xy 是被排序的 Array 的两个成员,因此由于这些成员具有相同的结构,您可以将 xy 解构为 { day: xDay }, { day: yDay } 并返回 @987654409 @ 以获得足够的结果。
在最后的 sn-p 中,我引入了一个新变量 cDay,它只是 day 属性的 String 表示。我还在 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”问题的方式可能是通过谷歌搜索,一旦您有了一些想法 - 检查适用于您的 Node 发行版的 ECMA 标准,以了解与您希望如何解决问题相关的任何细节。在这种情况下,如果您查找 ObjectsArray 的自定义排序,您会发现 Array.prototype.sort 及其工作原理。这应该有望引导您到其他 Array.prototype Functions 来完成其余的繁重工作。我尽力让我的回答引导你走上这条路。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-16
  • 2015-09-16
  • 2012-07-20
  • 2018-03-13
  • 2017-11-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多