简体   繁体   English

使用 javascript 循环遍历 json-Array

[英]loop through a json-Array with javascript

i have a problem looping through a json-Array with javascript in nodejs.我在nodejs中循环使用javascript的json数组时遇到问题。 my array called birthdaysArray looks like:我的名为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"}]

now i want to have an array which list the birthdays (sorted by the day of birth) of the current month.现在我想要一个列出当月生日(按出生日期排序)的数组。 entries with the same day and month (eg 23.04) should be listed in the same date.具有相同日期和月份(例如 23.04)的条目应在相同日期列出。 it should looks like, if april is the current month (the list has more than 100 entries.):它应该看起来像,如果4 月是当前月份(列表有 100 多个条目。):

[{"day":"15","name":["Mini Jall"]},{"day":"23", "name": ["Tom Smith","Michael Bird"]}]

i looked around, but i don't find a solution for that.我环顾四周,但我没有找到解决方案。 i checked this:我检查了这个:

for(var i = 0; i < json.length; i++) {
    var obj = json[i];
    console.log(obj.id);
}

but didn't match.但不匹配。 Who can help?谁能帮忙?

attachment 1: my code in the node_helper.js:附件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);
    })
}

=> output console.log: => output 控制台日志:

  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)

so, what can i do?那么,我该怎么办?

attachment 2: first I wanted to filter the string by month:附件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);

...and then apply the code (green hook, the second of them) to it. ...然后将代码(绿色钩子,第二个)应用到它。 unfortunately does not work as I had imagined can you do this now in one go?不幸的是不能像我想象的那样工作你现在可以在一个 go 中做到这一点吗? So filter from the annual list of birthdays by the current month, and then display these entries as I mentioned above.因此,按当月从年度生日列表中过滤,然后按我上面提到的那样显示这些条目。

Now that you changed the expected result - it's a little cleaner现在你改变了预期的结果——它更干净了

 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);

The goal is to create an Array of Object s who's structure is [{ day: String, name: [String] }, …] .目标是创建一个Object数组,其结构为[{ day: String, name: [String] }, …] Noting that repeated days will be grouped into the same Object who's day indicates the grouping.请注意,重复的日期将被分组到相同的Object中,日期指示分组。

The input data is an Array who's format is [{ birth: String, name: String }, …] .输入数据是一个数组,其格式为[{ birth: String, name: String }, …]


Firstly you will want to extract a meaningful day of the month out of a date string that isn't formatted in a standard way (eg, "25.04.1988" ). 首先,您需要从没有以标准方式格式化的日期字符串(例如"25.04.1988" )中提取一个有意义的日期。

To transform this String you can use a regular expression , with a callback parameter that orders the date in ISO-8601 Date format which the ECMA standard stipulates that Date must support in its construction.要转换此字符串,您可以使用带有回调参数的正则表达式,该参数以ISO-8601 日期格式对日期进行排序, ECMA标准规定Date在其构造中必须支持。 Note also that Date expects its input to be in UTC time, ie, it is ignorant of timezone.另请注意, Date期望其输入为 UTC 时间,即它不知道时区。

Such a construction may look like这样的结构可能看起来像

const input = new Date('23.04.1988'.replace(/^(\d+)[^\d+](\d+)[^\d+](\d+)$/, (...backRefs) => { return `${backRefs[3]}-${backRefs[2]}-${backRefs[1]}`; }));

The ECMAScript syntax (which Node.js implements) for declaring a regular expression literal is utilised here: /RegularExpressionBody/RegularExpressionFlags , where the flags are empty and the expression body is ^(\d+)[^\d+](\d+)[^\d+](\d+)$ .此处使用了用于声明正则表达式文字的 ECMAScript 语法( Node.js实现): /RegularExpressionBody/RegularExpressionFlags ,其中标志为空,表达式主体为^(\d+)[^\d+](\d+)[^\d+](\d+)$

This regular-expression does not match a valid date, but instead any construction of three series of numerals (\d+) , broken by non-numeric characters [^\d+] which constitutes an entire String .此正则表达式与有效日期不匹配,而是由三个数字系列(\d+)的任何构造匹配,由构成整个String的非数字字符[^\d+]打破。 It then reconstructs them using back-references in the order of 3-2-1 with dashes separating them as per the ISO-8601 Date 's format.然后,它使用3-2-1顺序的反向引用重建它们,并按照ISO-8601 Date的格式用破折号分隔它们。

That is the part `${backRefs[3]}-${backRefs[2]}-${backRefs[1]}` , which utilises Template Literals often referred to incorrectly as template string s.那就是`${backRefs[3]}-${backRefs[2]}-${backRefs[1]}`部分,它利用了经常被错误地称为模板字符串的模板文字。

Constructing a Date Object from this new variable const input will work, but it's toString() method will return the String "Invalid Date" if it is indeed, invalid.从这个新的变量const input构造一个Date Object是可行的,但是如果它确实是无效的,它的toString()方法将返回字符串"Invalid Date" If the input date does not match the regular expression, such that a back-reference indexes a capture group that doesn't capture;如果输入日期与正则表达式不匹配,则反向引用索引未捕获的捕获组; then the type of the constructed Date is undefined as it will be constructed with invalid inputs.那么构造的Date类型undefined的,因为它将使用无效的输入构造。 This particular result could be used to aggregate invalid birth-dates.此特定结果可用于汇总无效的出生日期。


Now that you know how to extract the day correctly, you can get to sorting and grouping the inputs. 现在您知道如何正确提取日期,您可以对输入进行排序和分组。

 const input = [{ "birth": "23.04.1988", "name": "Tom Smith" }, { "birth": "15.04.2010", "name": "Mini Jall" }, { "birth": "23.04.2001", "name":"Michael Bird" }];

Let input be your input Array of Object s.input成为Object的输入数组

The Array Object provides a Function called map on its prototype that you can use on input since it is an Array .阵列Object在其原型上提供了一个名为mapmap ,您可以在input上使用它,因为它是一个阵列 This Function 's specification is Array.prototype.map(callbackFn[, thisArg]) .这个Function的规范是Array.prototype.map(callbackFn[, thisArg]) It is called once for each element that exists in the Array which is the object being traversed .它为数组存在的每个元素调用一次,即被遍历的 object The returned value of the callback replaces the original object in a temporary Array which is returned by map upon completion.回调的返回值替换了临时数组中的原始 object,该临时数组map在完成时返回。 In other words, you can map your Array into a new structure, by returning that structure from the callback using each element's properties as you iterate.换句话说,您可以通过在迭代时使用每个元素的属性从回调中返回该结构来将您的数组map 转换为新结构。 Note that the argument thisArg is a context under which the map Function is invoked, and that if you call input.map then the context is inherited as input and so, thisArg is only optional.请注意,参数thisArg是调用map Function的上下文,并且如果您调用input.map则只有上下文作为input继承,因此thisArg是可选的。

Such an invocation would look like input.map(argumentsList) where argumentsList is a list that contains only a callback Function .这样的调用看起来像input.map(argumentsList)其中argumentsList是一个仅包含回调Function的列表。 The callback takes up to three parameters: currentValue, currentInndex, and the object being traversed.回调最多需要三个参数:currentValue、currentIndex 和正在遍历的 object。

So your callback should take the form of所以你的回调应该采取以下形式

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

In this callback, you want to transform the birth parameter to a day , so using the discussed methodology you would do something like在此回调中,您希望将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; };

We add 1 to the month because getUTCMonth returns a zero indexed month of the year.我们将1添加到月份,因为getUTCMonth返回一年中索引为零的月份。 Also we define the property month to be non-enumerable as we don't want it to show up in the result object.此外,我们将属性month定义为不可枚举,因为我们不希望它出现在结果 object 中。 Note also that the curr from earlier, is being destructured in { birth, name } and re-structured in { day: date.getDate(), month: date.getUTCMonth() + 1, name } .另请注意,之前的curr{ birth, name }中被解构并在{ day: date.getDate(), month: date.getUTCMonth() + 1, name }中重新构建。 Destructuring assignment allows you to reduce an Object 's properties into property names and in the argumentsList it declares those properties as variables in the scope of the arrow function.解构赋值允许您将Object属性减少为属性名称,并在argumentsList中将这些属性声明为箭头 ZC1C425268E68385D1AB4ZZC17A94F 的 scope 中的变量。 This is actually a shorthand for { birth: birth, name: name } since they have the same identifier as the properties of the input object curr .这实际上是{ birth: birth, name: name }的简写,因为它们具有与输入 object curr的属性相同的标识符。


At this point, you will have an Array of Object s. 此时,您将拥有一个Object数组

 [ { "day":23, "month": 4, "name": "Tom Smith" }, { "day": 15, "month": 4, "name": "Mini Jall" }, { "day": 23, "month": 4, "name": "Michael Bird" } ]

Except that the month properties will not be enumerable.除了不能枚举month属性。

You want to collate these Object s such that the String name is instead an Array of all name s who's parent Object shares identical day and month properties with other Object members of the Array .您想要整理这些Object s,以便String name是所有name s 的Array ,其父ObjectArray的其他Z497031794414A552435F9015B成员共享相同的daymonth属性。

So we will look to using Array.prototype.reduce which is defined as Array.prototype.reduce(callbackfn[, initialValue]) .所以我们将考虑使用定义为Array.prototype.reduce Array.prototype.reduce(callbackfn[, initialValue])的 Array.prototype.reduce。 The callback Function takes up to four parameters: previousValue, currentValue, currentIndex, and the object being traversed.回调Function最多需要四个参数:previousValue、currentValue、currentIndex 和正在遍历的 object。 If you provide initialValue then reduce iterates beginning at the first element, and the initialValue is provided to the callback as previousValue .如果您提供initialValue ,则reduce从第一个元素开始迭代,并将initialValue作为previousValue提供给回调。 However if you omit initialValue then reduce iterates beginning at the second element providing the first element as previousValue .但是,如果您省略initialValue ,则reduce从第二个元素开始的迭代,提供第一个元素作为previousValue What you return from the callback is then passed to the next iteration as its previousValue .然后,您从回调返回的内容将作为其previousValue传递给下一次迭代。

We'll also be using Array.prototype.findIndex which is defined as Array.prototype.findIndex(predicate[, thisArg ]) .我们还将使用定义为Array.prototype.findIndex Array.prototype.findIndex(predicate[, thisArg ])的 Array.prototype.findIndex。 The predicate Function takes up to three parameters and should return a boolean coercible result (such as 1 , 0 , true , false , undefined , etc).谓词Function最多需要三个参数,并且应该返回 boolean 强制结果(例如10truefalseundefined等)。 findIndex will return -1 if no predicate returns true , or it will return the index it reached when the first predicate does.如果没有谓词返回truefindIndex将返回-1 ,否则它将返回它在第一个谓词返回时达到的索引。

We can use this to find out if an array contains a matching day and month for an iteration of the reducer.我们可以使用它来确定数组是否包含匹配的daymonth ,以用于 reducer 的迭代。

 ({ day: tDay, month: tMonth, name: tName }) => { return tDay === … && tMonth === …; }

We want to construct a new Array output and we will iterate over the input using reduce , constructing the output as we go.我们要构造一个新的数组output 并且我们将使用reduce遍历输入,构造 output 就像我们 go 一样。 For this reason, reduce will be called with an empty array as its optional second argument出于这个原因,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; }

And the invocation looks like调用看起来像

input.map(dateCB).reduce(reducer, []);


Lastly we look at the function Array.prototype.sort defined as Array.prototype.sort(comparefn) . 最后我们看看 function Array.prototype.sort定义为Array.prototype.sort(comparefn) The comparefn Function receives two arguments, x and y . comparefn Function接收两个 arguments, xy The job of comparefn is to describe how x relates to y . comparefn的工作是描述xy的关系。 The sorter expects a negative Number returned from comparefn if x < y , zero if x == y and positive if x > y . 如果x < y ,则排序器期望从comparefn返回comparefn ,如果x == y则返回零,如果x > y则返回正数。 x and y are two members of the Array being sorted, and so since those members have the same structure you may destructure x and y as { day: xDay }, { day: yDay } and return xDay - yDay for a sufficient result. xy是要排序的Array的两个成员,因此由于这些成员具有相同的结构,您可以将xy解构为{ day: xDay }, { day: yDay }并返回xDay - yDay以获得足够的结果。
In this final snippet, I introduce a new variable cDay which is just a String representation of the day property. 在最后一个片段中,我引入了一个新变量cDay ,它只是day属性的字符串表示形式。 I also implement the non-enumerable property on the final Object s in the Array and the sorter on the output. 我还在数组中的最终Object和 output 上的排序器上实现了不可枚举的属性。
 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));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM