How to generate an array of unique objects from api? - javascript

I'm trying to disable booked times from calendar, depending on the date. My goal is to create an array, which holds objects with single date and array of booked times.
I have created an api, which outputs something like this:
"bookings": [
{
"_id": "5ce1b8792598adasf452",
"workType": "Nail polishing",
"client": "Mary Johnson",
"date": "2019-05-31T00:00:00.000Z",
"bookingTime": "09:00"
},
{
"_id": "5ce1b8753hs53gasf452",
"workType": "Makeup",
"client": "Kate Bush",
"date": "2019-05-31T00:00:00.000Z",
"bookingTime": "10:00"
}
]
I've tried using Sets, filters, but I just can't seem to wrap my head around how to implement it to my own code.
Snippet of my code:
bookedTimes: []
fetchBookedTimes() {
axios.get("http://localhost:8080/api/bookings").then(res => {
for (var i = 0; i < res.data.bookings.length; i++) {
this.bookedTimes.push({
date: moment(res.data.bookings[i].date).format("YYYY-MM-DD"),
times: [res.data.bookings[i].bookingTime.substring(0,2)]
});
}
});
}
I expect the output to be
bookedTimes: [
{
date: "2019-05-31",
times: ["09", "10"]
},
{
date: "2019-06-01",
times: ["10", "11"]
}
]
But the actual output is
bookedTimes: [
{
date: "2019-05-31",
times: ["09"]
},
{
date: "2019-05-31",
times: ["10"]
},
{
date: "2019-06-01",
times: ["10"]
},
{
date: "2019-06-01",
times: ["11"]
}
]

As per the code, the actual output is correct. You are looping the response and pushing the data to an array. If you want to group them by date, then you have to create an object and then convert it to the expected output.
var result = res.data.bookings.reduce(function (defaultValue, booking) {
var date = moment(booking.date).format("YYYY-MM-DD");
defaultValue[date] = defaultValue[date] || {date: date, times: []};
defaultValue[date].times.push(booking.bookingTime.substring(0,2));
return defaultValue;
}, {});
console.log(Object.values(result));

You can simply use reduce()
const arr = [
{
"_id": "5ce1b8792598adasf452",
"workType": "Nail polishing",
"client": "Mary Johnson",
"date": "2019-05-31T00:00:00.000Z",
"bookingTime": "09:00"
},
{
"_id": "5ce1b8753hs53gasf452",
"workType": "Makeup",
"client": "Kate Bush",
"date": "2019-05-31T00:00:00.000Z",
"bookingTime": "10:00"
},
{
"_id": "5ce1b8753hs53gasf452",
"workType": "Makeup",
"client": "Kate Bush",
"date": "2019-06-31T00:00:00.000Z",
"bookingTime": "11:00"
},
{
"_id": "5ce1b8753hs53gasf452",
"workType": "Makeup",
"client": "Kate Bush",
"date": "2019-06-31T00:00:00.000Z",
"bookingTime": "12:00"
}
]
const res = arr.reduce((ac,{date,bookingTime}) => {
ac[date] = ac[date] || {date,bookingTime:[]}
ac[date].bookingTime.push(bookingTime.slice(0,2));
return ac;
},{})
console.log(Object.values(res))

You're pushing values directly into array but you need to group them by date so you can use an object and then push values to array in the end
Here temp is used to group values by date
We check for date it exists we push the time value to times array if not we create a new property on temp
In the end we push values to this.bookedTimes array
fetchBookedTimes() {
axios.get("http://localhost:8080/api/bookings").then(res => {
let temp = {}
for (var i = 0; i < res.data.bookings.length; i++) {
let date = moment(res.data.bookings[i].date).format("YYYY-MM-DD"),
let time = [res.data.bookings[i].bookingTime.substring(0,2)]
temp[date] = temp[date] || {date: date, times:[]}
temp[date].times.push(time)
});
}
this.bookedTimes.push(Object.values(temp))
});
}

First, check if the date of already in the array. Check if 'times' already exist in 'object.times', if not, push it to the 'object.times' array.
Please see the code below.
const date = moment(res.data.bookings[i].date).format("YYYY-MM-DD");
const times = res.data.bookings[i].bookingTime.substring(0, 2);
const arrayIndex = bookedTimes.findIndex(item => item.date === date);
//Check if date already exist in array
if (arrayIndex !== -1) {
//Check if 'times' already exist in 'object.times'
if (!bookedTimes[arrayIndex].times.includes(times)) {
//Push 'times' in 'object.times'
bookedTimes[arrayIndex].times.push(times);
}
} else {
//Push a new object into the array
bookedTimes.push({
date: date,
times: [times]
});
}

Related

Is there way to rearange objects in array that the value becomes the key?

I'm stuck on this type of situation where the values of the object is changed to a different value. Is there way to shift a value to a key or would simply deleting and adding be better? I tried to loop to see which of the keys overlap in value and using the if statement and conditions i tried adding or deleting using Array methods. However, since the inter data is an object i am sruggling to find the right methods or even the process. I also tried using a function to insert the data and pushing to a new empty array that is returned from the function.
If I have objects in an array like so:
const data = [
{
"date": "12/22",
"treatment": "nausea",
"count": 2
},
{
"date": "12/23",
"treatment": "cold",
"count": 3
},
{
"date": "12/22",
"treatment": "cold",
"count": 2
}
];
and wanting to change the data like so:
const newData = [
{
"date": "12/22",
"cold": 2
"nausea": 2,
},
{
"date": "12/23",
"cold": 3
}
];
try this code using loop and reduce and every time add to new array
const data = [
{
"date": "12/22",
"treatment": "nausea",
"count": 2
},
{
"date": "12/23",
"treatment": "cold",
"count": 3
},
{
"date": "12/22",
"treatment": "cold",
"count": 2
}
];
const newData = [];
const dataByDate = data.reduce((acc, curr) => {
if (!acc[curr.date]) {
acc[curr.date] = { date: curr.date };
}
acc[curr.date][curr.treatment] = curr.count;
return acc;
}, {});
for (let date in dataByDate) {
newData.push(dataByDate[date]);
}
console.log(newData);
We want to reduce the data by unique dates. This can be done with:
An object as a dictionary,
Set or Map, or
Some other custom implementation.
Prefer to use Array.reduce() when reducing an array. This is standardized and more expressive than a custom implementation.
Using a map-like structure as the accumulator allows reduction of the dates by uniqueness and the data itself, simultaneously.
Note: Properties of objects are converted to Strings (except for Symbols). So if you want to use different "keys" that are equal after conversion (e.g. 0 and "0"), you cannot use objects; use Map instead.
(All our dates are Strings already, so this warning does not apply here.)
When using an object we can use the nullish coalescing assignment ??=: This allows us to assign an initial "empty" entry ({ date: dataEntry.date }) when encountering a new unique date.
Further, that assignment evaluates to the dictionary's entry; the entry that was either already present or just assigned.
Then we only need to assign the treatment and its count as a key-value pair to the entry.
const data = [
{ "date": "12/22", "treatment": "nausea", "count": 2 },
{ "date": "12/23", "treatment": "cold", "count": 3 },
{ "date": "12/22", "treatment": "cold", "count": 2 }
];
const newData = reduceByDate(data);
console.log(newData);
function reduceByDate(data) {
const dataByDate = data.reduce((dict, dataEntry) => {
const dictEntry = dict[dataEntry.date] // Get existing or ...
??= { date: dataEntry.date }; // ... just initialized entry.
dictEntry[dataEntry.treatment] = dataEntry.count;
return dict;
}, {});
// Transform dictionary to array of reduced entries
return Object.values(dataByDate);
}
You can make use of reduce() and Object.assign().
First we use reduce to combine objects with the same date into one object and then use assign to merge the values:
const data = [{
"date": "12/22",
"treatment": "nausea",
"count": 2
},
{
"date": "12/23",
"treatment": "cold",
"count": 3
},
{
"date": "12/22",
"treatment": "cold",
"count": 2
}
];
const newData = data.reduce((acc, curr) => {
const dateIndex = acc.findIndex(item => item.date === curr.date);
if (dateIndex === -1) {
acc.push({
date: curr.date,
[curr.treatment]: curr.count
});
} else {
acc[dateIndex] = Object.assign({}, acc[dateIndex], {
[curr.treatment]: curr.count
});
}
return acc;
}, []);
console.log(newData)

Sort max date from Object

Trying to sort the object with the max date. One id may have multiples dates. Below is the format of the object where id:123 has two dates. So I am trying to take the max date for the user 123. I used the sort method and storing the array[0] but still there is something missing.
var arr = [
{
"scores": [
{
"score": 10,
"date": "2021-06-05T00:00:00"
}
],
"id": "3212"
},
{
"scores": [
{
"score": 10,
"date": "2021-06-05T00:00:00"
},
{
"score": 20,
"date": "2021-05-05T00:00:00"
}
],
"id": "123"
},
{
"scores": [
{
"score": 5,
"date": "2021-05-05T00:00:00"
}
],
"id": "321"
}
]
What I tried is
_.each(arr, function (users) {
users.scores = users.scores.filter(scores => new Date(Math.max.apply(null, scores.date)));
return users;
});
Expecting the output to look like the following with the max date selected.
[
{
"scores": [
{
"score": 10,
"date": "2021-06-05T00:00:00"
}
],
"id": "3212"
},
{
"scores": [
{
"score": 10,
"date": "2021-06-05T00:00:00"
}
],
"id": "123"
},
{
"scores": [
{
"score": 5,
"date": "2021-05-05T00:00:00"
}
],
"id": "321"
}
]
Your filter callback function is not performing a comparison to filter the correct element. Also, although applying the "maximum" algorithm on the dates as string would be fine in your case (because of the date format you have), it would be much safer to transform the date strings into date objects to consistantly get correct results regardless of the format.
In the solution below, you can use a combination of Array.map() and Array.sort() to copy and process your data in the correct result.
const data = [{
'scores': [{
'score': 10,
'date': '2021-06-05T00:00:00'
}],
'id': '3212'
}, {
'scores': [{
'score': 10,
'date': '2021-06-05T00:00:00'
}, {
'score': 20,
'date': '2021-05-05T00:00:00'
}],
'id': '123'
}, {
'scores': [{
'score': 5,
'date': '2021-05-05T00:00:00'
}],
'id': '321'
}];
// map the data and return the updated objects as the result
const result = data.map((user) => {
// copy the scores array to not mutate the original data
const sortedScores = user.scores.slice();
// sort the scores array by date descending
sortedScores.sort((a, b) => (new Date(b.date) - new Date(a.date)));
// return the same user with the first score from the sorted array
return {
...user,
scores: [sortedScores[0]]
};
});
console.log(result);

Filter out objects in array if one value is the same

I am fetching data from an api that, sometimes, gives me multiple objects with the same values, or very similar values, which I want to remove.
For example, I might get back:
[
{
"Name": "blah",
"Date": "1992-02-18T00:00:00.000Z",
"Language": "English",
},
{
"Name": "blahzay",
"Date": "1998-02-18T00:00:00.000Z",
"Language": "French",
}, {
"Name": "blah", // same name, no problem
"Date": "1999-02-18T00:00:00.000Z", // different date
"Language": "English", // but same language
},
]
So I want to check that no two objects have a key with the same "Language" value (in this case, "English").
I would like to get the general process of filtering out the entire object if it's "Language" value is duplicated, with the extra issue of not having the same number of objects returned each time. So, allowing for dynamic number of objects in the array.
There is an example here:
Unexpeected result when filtering one object array against two other object arrays
but it's assuming that you have a set number of objects in the array and you are only comparing the contents of those same objects each time.
I would be looking for a way to compare
arrayName[eachObject].Language === "English"
and keep one of the objects but any others (an unknown number of objects) should be filtered out, most probably using .filter() method along with .map().
The below snippets stores the languages that have been encountered in an array. If the current objects language is in the array then it is filtered out. It makes the assumption that the first object encountered with the language is stored.
const objs = [
{
"Name": "blah",
"Date": "1992-02-18T00:00:00.000Z",
"Language": "English",
},
{
"Name": "blahzay",
"Date": "1998-02-18T00:00:00.000Z",
"Language": "French",
}, {
"Name": "blah", // same name, no problem
"Date": "1999-02-18T00:00:00.000Z", // different date
"Language": "English", // but same language
},
],
presentLanguages = [];
let languageIsNotPresent;
const objsFilteredByLanguage = objs.filter(function (o) {
languageIsNotPresent = presentLanguages.indexOf(o.Language) == -1;
presentLanguages.push(o.Language);
return languageIsNotPresent;
});
console.log(objsFilteredByLanguage);
You could take a hash table and filter the array by checking Name and Language.
var array = [{ Name: "blah", Date: "1992-02-18T00:00:00.000Z", Language: "English" }, { Name: "blahzay", Date: "1998-02-18T00:00:00.000Z", Language: "French" }, { Name: "blah", Date: "1999-02-18T00:00:00.000Z", Language: "English" }],
hash = {},
result = array.filter(({ Name, Language }) => {
var key = `${Name}|${Language}`;
if (!hash[key]) return hash[key] = true;
});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Using Set makes it easy to remove duplicates for as many keys as you like. I tried to be as verbose as possible so that each step was clear.
var objects = [{ "Name": "blah", "Date": "1992-02-18T00:00:00.000Z", "Language": "English", }, { "Name": "blah", "Date": "1998-02-18T00:00:00.000Z", "Language": "French", }, { "Name": "blah", "Date": "1999-02-18T00:00:00.000Z", "Language": "English" }];
function uniqueKeyVals(objects, key) {
const objVals = objects.map(object => object[key]); // ex. ["English", "French", "English"]
return objects.slice(0, new Set(objVals).size); // ex. { "English", "French" }.size = 2
}
function removeKeyDuplicates(objects, keys) {
keys.forEach(key => objects = uniqueKeyVals(objects, key));
return objects;
}
// can also use uniqueKeyVals(key) directly for just one key
console.log("Unique 'Language': \n", removeKeyDuplicates(objects, ["Language"]));
console.log("Unique ['Language', 'Name']: \n", removeKeyDuplicates(objects, ["Language", "Name"]));
I would use the underscore module for JavaScript and the unique function in this scenario. Here is a sample array of data objects:
let data = [{
name: 'blah',
date: Date.now(),
language: "en"
},
{
name: 'noblah',
date: Date.now(),
language: 'es'
},
{
name: 'blah',
date: Date.now(),
language: 'en'
}];
Then we can use the unique function in the underscore library to only return a copy of the data that has unique values associated with the language key:
const result = _.unique(data, 'language');

Sorting array of object javascript / typescript on firestore timestamp

I have below array structure
[
{
"id": "8gFUT6neK2I91HIVkFfy",
"element": {
"id": "8gFUT6neK2I91HIVkFfy",
"archived": false,
"updatedOn": {
"seconds": 1538653447,
"nanoseconds": 836000000
}
},
"groupBy": "pr"
},
{
"id": "9jHfOD8ZIAOX4fE1KUQc",
"element": {
"id": "9jHfOD8ZIAOX4fE1KUQc",
"archiveDate": {
"seconds": 1539250407,
"nanoseconds": 62000000
},
"archived": false,
"updatedOn": {
"seconds": 1538655984,
"nanoseconds": 878000000
}
},
"groupBy": "pr"
},
{
"id": "CeNP27551idLysSJOd5H",
"element": {
"id": "CeNP27551idLysSJOd5H",
"archiveDate": {
"seconds": 1539248724,
"nanoseconds": 714000000
},
"archived": false,
"updatedOn": {
"seconds": 1538651075,
"nanoseconds": 235000000
}
},
"groupBy": "pr"
},
{
"id": "Epd2PVKyUeAmrzBT3ZHT",
"element": {
"id": "Epd2PVKyUeAmrzBT3ZHT",
"archiveDate": {
"seconds": 1539248726,
"nanoseconds": 226000000
},
"archived": false,
"updatedOn": {
"seconds": 1538740476,
"nanoseconds": 979000000
}
},
"groupBy": "pr"
}
]
and below code to sort
Sample JSfiddle
http://jsfiddle.net/68wvebpz/
let sortedData = this.arraydata.sort((a:any, b:any) => { return Number(new Date(b.element.date).getTime()) - Number(new Date(a.element.date).getTime()) })
This does not make any effect.
There are a few problems that we need to fix:
Your updatedOn object is not something that can be converted to a date. You need to do extra work.
JavaScript doesn't support nanoseconds, only milliseconds. You will therefore need to divide that number by a million.
By using getTime for the comparison, you're actually discarding the milliseconds - that function returns seconds.
To fix the first two, use this function:
function objToDate(obj) {
let result = new Date(0);
result.setSeconds(obj.seconds);
result.setMilliseconds(obj.nanoseconds/1000000);
console.log('With nano', result);
return result;
}
This creates a new date and then sets the seconds and milliseconds. This gives you dates in October 2018 when I use your test data.
Then, to compare them and fix the remaining problems, use this (much simpler) form:
let sortedData = data.sort((a:any, b:any) => {
let bd = objToDate(b.element.updatedOn);
let ad = objToDate(a.element.updatedOn);
return ad - bd
});
That should do it.
To reverse the sort order, just use the less-than operator:
return bd - ad
Turn your strings into dates, and then subtract them to get a value that is either negative, positive, or zero:
array.sort(function(a,b){
return new Date(b.date) - new Date(a.date);
});
Is it something like this:
var array = [
{id: 1, name:'name1', date: 'Mar 12 2012 10:00:00 AM'},
{id: 2, name:'name2', date: 'Mar 8 2012 08:00:00 AM'}
];
console.log(array.sort((a, b) => {
return new Date(a.date) - new Date(b.date)
}))

Javascript parse Response Object into Array of Objects

Below is the response from Paypal's NVP API. I am not familiar with working with a response that is a single object of keys/values like this. Typically I would expect for a search to be return in a form of an array with objects within.
Is there a way for me to turn this into an Array of Objects?
{
L_TIMESTAMP0: "2018-05-08T19:23:17Z",
L_TIMESTAMP1: "2018-05-08T18:50:01Z",
L_TIMESTAMP2: "2018-05-08T18:45:30Z",
L_TIMEZONE0: "GMT",
L_TIMEZONE1: "GMT",
L_TIMEZONE2: "GMT",
L_TYPE0: "Payment",
L_TYPE1: "Payment",
L_TYPE2: "Payment",
L_NAME0: "Person One",
L_NAME1: "Person Two",
L_NAME2: "Person Three",
L_TRANSACTIONID0: "*********",
L_TRANSACTIONID1: "*********",
L_TRANSACTIONID2: "*********",
L_STATUS0: "Completed",
L_STATUS1: "Completed",
L_STATUS2: "Completed",
L_AMT0: "10.00",
L_AMT1: "100.00",
L_AMT2: "1000.00",
L_CURRENCYCODE0: "USD",
L_CURRENCYCODE1: "USD",
L_CURRENCYCODE2: "USD",
L_FEEAMT0: "-0.29",
L_FEEAMT1: "-2.93",
L_FEEAMT2: "-29.30",
L_NETAMT0: "9.71",
L_NETAMT1: "97.70",
L_NETAMT2: "970.70",
TIMESTAMP: "2018-05-08T19:47:10Z", // not for array
CORRELATIONID: "*******", // not for array
ACK: "Success", // not for array
VERSION: "204", // not for array
BUILD: "39949200" // not for array
}
I would like parse this into an array of objects:
const recentOrders = [{
timestamp: L_TIMESTAMP0,
timezone: L_TIMEZONE0,
type: L_TYPE,
.....
},
{ timestamp: L_TIMESTAMP1, .... },
{ timestamp: L_TIMESTAMP2, .... },
// .... and so forth
]
Something like this should work:
var data = {
L_TIMESTAMP0: "2018-05-08T19:23:17Z",
L_TIMESTAMP1: "2018-05-08T18:50:01Z",
L_TIMESTAMP2: "2018-05-08T18:45:30Z",
L_TIMEZONE0: "GMT",
L_TIMEZONE1: "GMT",
L_TIMEZONE2: "GMT",
L_TYPE0: "Payment",
L_TYPE1: "Payment",
L_TYPE2: "Payment",
L_NAME0: "Person One",
L_NAME1: "Person Two",
L_NAME2: "Person Three",
L_TRANSACTIONID0: "*********",
L_TRANSACTIONID1: "*********",
L_TRANSACTIONID2: "*********",
L_STATUS0: "Completed",
L_STATUS1: "Completed",
L_STATUS2: "Completed",
L_AMT0: "10.00",
L_AMT1: "100.00",
L_AMT2: "1000.00",
L_CURRENCYCODE0: "USD",
L_CURRENCYCODE1: "USD",
L_CURRENCYCODE2: "USD",
L_FEEAMT0: "-0.29",
L_FEEAMT1: "-2.93",
L_FEEAMT2: "-29.30",
L_NETAMT0: "9.71",
L_NETAMT1: "97.70",
L_NETAMT2: "970.70",
TIMESTAMP: "2018-05-08T19:47:10Z", // not for array
CORRELATIONID: "*******", // not for array
ACK: "Success", // not for array
VERSION: "204", // not for array
BUILD: "39949200" // not for array
};
const recentOrders = [];
var keys = Object.keys(data).filter(r => r.startsWith('L_'));
keys.forEach(k => {
var index = parseInt(k.replace(/\D/g,''));
var newKey = k.substring(2).replace(/[0-9]/g, '').toLowerCase();
if (recentOrders[index] === undefined) {
recentOrders[index] = {};
}
recentOrders[index][newKey] = data[k];
});
console.log(recentOrders);
I'm not entirely sure what the output you want is, but fundamentally you want to map and transform the data from the very flat structure you are given to more of a list of related objects.
You can do this using some of the basic native methods of arrays to filter map and reorganize your data. The following code basically does the following:
Get the keys of the object
Filter the keys to the numbered ones you want
Map the keys to a small informational object about each
Reorganize the list of keys into a list of objects with the original indexed data in the objects at the correct index.
This can be done in one line with the end result being a a recentOrders variable set to an array of 3 objects with the named keys lowercased (which I think is what you are after)
const recentOrders = Object.keys(data)
.filter(function (key) {
return key.startsWith('L_')
})
.map(function (key) {
const parts = key.match(/^L_([A-Z]+)(\d+)$/);
return {
key: parts[1].toLowerCase(),
value: data[key],
index: parseInt(parts[2])
};
})
.reduce(function (recentOrders, item) {
if (recentOrders[item.index] == null) {
recentOrders[item.index] = {}
}
recentOrders[item.index][item.key] = item.value;
return recentOrders ;
}, []);
console.log(recentOrders);

Categories