i have a problem looping through a json-Array with javascript in nodejs. my array called birthdaysArray looks like:
[{"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. it should looks like, if april is the current month (the list has more than 100 entries.):
[{"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:
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:
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:
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? 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] }, …]
. Noting that repeated days will be grouped into the same Object who's day indicates the grouping.
The input data is an Array who's format is [{ birth: String, name: String }, …]
.
"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. Note also that Date expects its input to be in UTC time, ie, it is ignorant of timezone.
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+)$
.
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 . 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.
That is the part `${backRefs[3]}-${backRefs[2]}-${backRefs[1]}`
, which utilises Template Literals often referred to incorrectly as template string s.
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. 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. This particular result could be used to aggregate invalid birth-dates.
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.
The Array Object provides a Function called map
on its prototype that you can use on input
since it is an Array . This Function 's specification is Array.prototype.map(callbackFn[, thisArg])
. It is called once for each element that exists in the Array which is the object being traversed . The returned value of the callback replaces the original object in a temporary Array which is returned by map
upon completion. 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. 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.
Such an invocation would look like input.map(argumentsList)
where argumentsList is a list that contains only a callback Function . The callback takes up to three parameters: currentValue, currentInndex, and the object being traversed.
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
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. Also we define the property month
to be non-enumerable as we don't want it to show up in the result 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 }
. 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. This is actually a shorthand for { birth: birth, name: name }
since they have the same identifier as the properties of the input object curr
.
[ { "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.
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 .
So we will look to using Array.prototype.reduce
which is defined as Array.prototype.reduce(callbackfn[, initialValue])
. The callback Function takes up to four parameters: previousValue, currentValue, currentIndex, and the object being traversed. If you provide initialValue
then reduce
iterates beginning at the first element, and the initialValue
is provided to the callback as previousValue
. However if you omit initialValue
then reduce
iterates beginning at the second element providing the first element as previousValue
. What you return from the callback is then passed to the next iteration as its previousValue
.
We'll also be using Array.prototype.findIndex
which is defined as Array.prototype.findIndex(predicate[, thisArg ])
. The predicate Function takes up to three parameters and should return a boolean coercible result (such as 1
, 0
, true
, false
, undefined
, etc). findIndex
will return -1
if no predicate returns true
, or it will return the index it reached when the first predicate does.
We can use this to find out if an array contains a matching day
and month
for an iteration of the 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. For this reason, reduce will be called with an empty array as its optional second argument
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, []);
Array.prototype.sort
defined as Array.prototype.sort(comparefn)
.
The comparefn
Function receives two arguments, x
and y
.
The job of comparefn
is to describe how x
relates to y
.
The sorter expects a negative Number returned from comparefn
if x < y
, zero if x == y
and positive if 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.
cDay
which is just a String representation of the day
property.
I also implement the non-enumerable property on the final Object s in the Array and the sorter on the 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));
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.