Comparing two arrays of objects and removing when two match - javascript

I was wondering if anyone could help me. I am using React and I have two arrays of objects:
one for all possible booking times
the other for existing bookings.
I am struggling to find a way of successfully looping over the allSlots array and removing any that have a matching time inside the existingBookings array.
In the example below, there are existing bookings at 10:00am, 10:40am, 11:00am and 11:20am.
The expected output would only leave 10:20am and 11:40am from the original array.
const allSlots = [
{
date: "28 Sept 22",
time: "10:00am"
},
{
date: "28 Sept 22",
time: "10:20am"
},
{
date: "28 Sept 22",
time: "10:40am"
},
{
date: "28 Sept 22",
time: "11:00am"
},
{
date: "28 Sept 22",
time: "11:20am"
},
{
date: "28 Sept 22",
time: "11:40am"
}
];
const existingBookings = [
{
time: "10:00am",
propertyID: "XQPvl7MmLVNtxHdSRfDq",
userID: "Bq4b3uz129aE2D5TCbaOiLQJrvC2",
date: "28 Sept 22"
},
{
time: "11:00am",
propertyID: "XQPvl7MmLVNtxHdSRfDq",
userID: "Ko2LdnQAdaE2OiLQJrvC2D5TCbA",
date: "28 Sept 22"
},
{
time: "10:40am",
propertyID: "XQPvl7MmLVNtxHdSRfDq",
userID: "Ko2LdnQAdaE2OiLQJrvC2D5TCbA",
date: "28 Sept 22"
},
{
time: "11:20am",
propertyID: "XQPvl7MmLVNtxHdSRfDq",
userID: "iLQJrKo2LdCbnQAdaE2OvC2D5TA",
date: "28 Sept 22"
}
];
I originally filtered the existingBookings data down to remove any that did not match the selected date with:
const existingBookings = allBookings.filter(
(booking) => booking.date === selectedDate
);
However, I am struggling to manipulate things further. I really appreciate any insight and help you may be able to give.

It is as simple as doing
freeSlots=allSlots.filter(s=>
existingBookings.every(e=>e.time!=s.time||e.date!=s.date)
)
Here is a working (plain) JavaScript snippet:
const allSlots = [
{
date: "28 Sept 22",
time: "10:00am"
},
{
date: "28 Sept 22",
time: "10:20am"
},
{
date: "28 Sept 22",
time: "10:40am"
},
{
date: "28 Sept 22",
time: "11:00am"
},
{
date: "28 Sept 22",
time: "11:20am"
},
{
date: "28 Sept 22",
time: "11:40am"
}
];
const existingBookings = [
{
time: "10:00am",
propertyID: "XQPvl7MmLVNtxHdSRfDq",
userID: "Bq4b3uz129aE2D5TCbaOiLQJrvC2",
date: "28 Sept 22"
},
{
time: "11:00am",
propertyID: "XQPvl7MmLVNtxHdSRfDq",
userID: "Ko2LdnQAdaE2OiLQJrvC2D5TCbA",
date: "28 Sept 22"
},
{
time: "10:40am",
propertyID: "XQPvl7MmLVNtxHdSRfDq",
userID: "Ko2LdnQAdaE2OiLQJrvC2D5TCbA",
date: "28 Sept 22"
},
{
time: "11:20am",
propertyID: "XQPvl7MmLVNtxHdSRfDq",
userID: "iLQJrKo2LdCbnQAdaE2OvC2D5TA",
date: "28 Sept 22"
}
];
const freeSlots=allSlots.filter(s=>
existingBookings.every(e=>e.time!=s.time||e.date!=s.date)
)
console.log(freeSlots);

You want to filter your allSlots array so that it only contains slots not present in existingBookings
const unusedSlots = allSlots.filter((slot) => {
// See if the slot is booked
const isBooked = existingBookings.some(
(booking) => booking.time == slot.time && booking.date == slot.date
)
// Only keep free slots
return !isBooked
});

This should work.
const newAllSlots = allSlots.filter((slot)=>{
const iSInExistingBookings = existingBookings.find(booking=>booking.time === slots.time && booking.date === slots.date)
return !iSInExistingBookings
})

Since you know what you already have booked with existingBookings, I would suggest looping through that first. Then you can recursively filter the allSlots array back into itself by checking if the date matches but the time does not. Once completed, the allSlots array should only have the date and times that haven't been booked.
existingBookings.forEach(booking => {
allSlots = allSlots.filter(slot => {
return booking.date === slot.date && booking.time !== slot.time;
});
});
Edit: You will have to scope allSlots with let instead of const for this to work. You could also assign allSlots to another variable if you want to keep allSlots in tact:
let slots = allSlots;
existingBookings.forEach(booking => {
slots = slots.filter(slot => {
return booking.date === slot.date && booking.time !== slot.time;
});
});

Related

map on the lower level key of a nested object

I had posted this question earlier but someone deleted with How can I access and process nested objects, arrays or JSON?
as possible answer. This is not helping me since a) The key 'date' is spread across several names, b)The objects comprises on arrays & objects & c) The depth at which the key 'date' is present can change.
Hence posting the question again.
I have a JS object as below
const bb = {
Harry: {
details: [
{
cat: "Life",
values: [
{
date: "2021-08-02",
level: 77.6,
},
{
date: "2021-08-03",
level: 79.1,
},
],
},
],
},
Barry: {
details: [
{
cat: "Logic",
values: [
{
date: "2021-08-02",
level: 77.6,
},
{
date: "2021-08-03",
level: 79.1,
},
],
},
],
},
};
I also have a variable defined for parsing the dates const dateParse = d3.timeParse("%Y-%m-%d")
I want to parse all the dates in the object. Since the 'date' is few levels below in the object, I am not able to figure this out. How do I go about it?
The expected output is
const bb = {
Harry: {
details: [
{
cat: "Life",
values: [
{
date: Mon Aug 02 2021 00:00:00 GMT+0530 (India Standard Time),
level: 77.6,
},
{
date: Tue Aug 03 2021 00:00:00 GMT+0530 (India Standard Time),
level: 79.1,
},
],
},
],
},
Barry: {
details: [
{
cat: "Logic",
values: [
{
date: Mon Aug 02 2021 00:00:00 GMT+0530 (India Standard Time),
level: 77.6,
},
{
date: Tue Aug 03 2021 00:00:00 GMT+0530 (India Standard Time),
level: 79.1,
},
],
},
],
},
};
You just have to loop through the objects, select the date node and execute
(new Date(datestring)).toString()
This will generate the date as specified in your output.
const bb = { Harry: { details: [{cat: "Life",values: [{date: "2021-08-02",level: 77.6},{date: "2021-08-03",level: 79.1}]}]},Barry: {details: [{cat: "Logic",values: [{date: "2021-08-02",level: 77.6},{date: "2021-08-03",level: 79.1}]}]}};
Object.values(bb).forEach((value) => {
value.details.forEach((detail) => {
detail.values.forEach((value) => {
value.date = (new Date(value.date)).toString();
})
})
});
console.log(bb);
If you want to parse all the nested date keys, you can do it recursively using the below functions
const bb = { Harry: { details: [{cat: "Life",values: [{date: "2021-08-02",level: 77.6},{date: "2021-08-03",level: 79.1}]}]},Barry: {details: [{cat: "Logic",values: [{date: "2021-08-02",level: 77.6},{date: "2021-08-03",level: 79.1}]}]}};
function traverseArray(inputArray) {
for (const arrayValue of inputArray) {
traverseObject(arrayValue);
}
}
function traverseObject(inputObject) {
for (const key in inputObject) {
const value = inputObject[key];
const valueType = typeof(value);
if (valueType == "object") {
traverseObject(value);
} else if (valueType == "array") {
traverseArray(value);
} else if (key == "date") { // since I want to parse only date keys
inputObject[key] = 'd3.timeParse("%Y-%m-%d") - Parse date here';
}
}
}
traverseObject(bb);
console.log(bb);

Get common data from multiple arrays of object that containing a unique id and nested array using javascript es6

I am trying to find the common data from multiple arrays of object that contains a unique id (mongoose object id) and an array of string.
for example:
array1 = [{
_id: "60f027f98b55eb2df1f36c04",
date: "2021-07-15T12:18:12.223Z",
time: "30",
hours: ["8:00 AM", "8:30 AM"]
}]
array2 = [{
_id: "60f027f98b55eb2df1f36c05",
date: "2021-07-15T12:18:12.223Z",
time: "60",
hours: ["7:30 AM", "8:30 AM", "9:30AM"]
}]
array3 = [{
_id: "60f027f98b55eb2df1f36c06",
date: "2021-07-16T12:12:12.223Z",
time: "30",
hours: ["7:00 AM", "8:30 AM"]
}]
The output should have maximum common values in the arrays for maximum common dates and that date should have maximum common hour.
So the sample output should look something like this.
common_data = {
date: "2021-07-15T12:18:12.223Z",
time: "30",
hours: "8:30AM"
}
I looked up at other answers and tried something like this:
merged all the arrays and
let result = merged_slots_array.shift().filter(function(v) {
return merged_slots_array.every(function(a) {
const matchDate = a.date === v.date;
const getMatchTime = a.hours.shift().filter(function(x) {
return v.hours.every(function(t) {
return x.indexOf(t) !== -1;
})
});
return matchDate && getMatchTime
});
});
but getting error merged_slots_array.shift(...).filter is not a function
After concatenating the arrays, finding the max common hour can be done through a filter that only keeps duplicates, then gets sorted. Once we have that, we can query each array to make sure it contains the max hour, then extract the max date and time. My output was slightly different than yours because i filtered for the max time, hour and date
array1 = [{
_id: "60f027f98b55eb2df1f36c04",
date: "2021-07-15T12:18:12.223Z",
time: "30",
hours: ["8:00 AM", "8:30 AM"]
}]
array2 = [{
_id: "60f027f98b55eb2df1f36c05",
date: "2021-07-15T12:18:12.223Z",
time: "60",
hours: ["7:30 AM", "8:30 AM", "9:30AM"]
}]
array3 = [{
_id: "60f027f98b55eb2df1f36c06",
date: "2021-07-16T12:12:12.223Z",
time: "30",
hours: ["7:00 AM", "8:30 AM"]
}]
const getCommon = (arrays) => {
let group = [].concat.apply([], [...arrays])
let hour = group.map(e=>e.hours).flat().filter((e,i,a) => a.indexOf(e) !== i).sort((a,b) => a.localeCompare(b))[0]
let common = group.filter(e=>e.hours.includes(hour))
let time = Math.max(...common.map(e => +e.time))
let date = common.map(e => e.date).sort((a,b) => new Date(b) - new Date(a))[0];
return {date: date, time: time, hours: [hour]}
}
let arg = []
arg.push(array1)
arg.push(array2)
arg.push(array3)
console.log(getCommon(arg))
TS Playground

Javascript : Filter list by last time of day and return a single value

I have a list of times of text messages and I am trying to return the last text message sent on a particular day.
I am new to Javascript maybe this is easy but I just don't know how to do this
This what I have tried I am completely clueless.
My Code:
const texts = [
{
date: "27 Jan 2021",
text: "Hey nice to see you",
time: "12:22",
},
{
date: "27 Jan 2021",
feeling: "Cry",
text: "All good you?",
time: "16:26",
},
{
date: "27 Jan 2021",
text: "You too how's life treating you?",
time: "12:26",
},
]
const output = texts.filter(i=>i.time)
Desired Output:
const output = [
{
date: "27 Jan 2021",
feeling: "Cry",
text: "All good you?",
time: "16:26",
},
]
Sorting and then taking first / last depending on how you sorted is a popular way, but it's not the most efficient if the array is very large.
But you could use reduce to keep the accumulator with the maximum value.
eg.
const texts = [{"date":"27 Jan 2021","text":"Hey nice to see you","time":"12:22"},{"date":"27 Jan 2021","feeling":"Cry","text":"All good you?","time":"16:26"},{"date":"27 Jan 2021","text":"You too how's life treating you?","time":"12:26"}];
const result = texts.reduce((a, v) => {
return v.time > a.time ? v : a;
});
console.log([result]);
As noted in comments, make sure your times are zero prefixed, it's generally the case as it's the ISO standard, but worth checking just in case.
If your times are not zero prefixed, as mentioned in comments, a simple solution would be to convert to minutes and compare with this. Another option is to prefix with zero using padLeft
You can sort and get the first element.
This is assuming the time property follows this format -> hh24:mm and the date property references to a specific day only.
const texts = [
{
date: "27 Jan 2021",
text: "Hey nice to see you",
time: "12:22",
},
{
date: "27 Jan 2021",
feeling: "Cry",
text: "All good you?",
time: "16:26",
},
{
date: "27 Jan 2021",
text: "You too how's life treating you?",
time: "12:26",
}
]
let [output] = texts.sort((a, b) => b.time.localeCompare(a.time));
console.log(output);
Considering you said "last text message sent on a particular day" I'd first filter for that date, then sort them and then get the last entry's text.
Something like:
const texts = [
{
date: "27 Jan 2021",
text: "Hey nice to see you",
time: "12:22",
},
{
date: "27 Jan 2021",
feeling: "Cry",
text: "All good you?",
time: "16:26",
},
{
date: "27 Jan 2021",
text: "You too how's life treating you?",
time: "12:26",
},
]
const date = "27 Jan 2021";
const output = texts
.filter(
(text) => text.date = date
).sort(
(textA, textB) => textA.time.localeCompare(textB.time)
).pop().text || 'No text found for this date';
console.log(output);
you should try this..
const texts = [
{
date: "27 Jan 2021",
text: "Hey nice to see you",
time: "12:22",
},
{
date: "27 Jan 2021",
feeling: "Cry",
text: "All good you?",
time: "16:26",
},
{
date: "27 Jan 2021",
text: "You too how's life treating you?",
time: "12:26",
}];
var lastMsg = [];
texts.forEach(element => {
lastMsg = element
});
console.log(lastMsg);

Compare two object arrays and get the difference in Javascript

I have two arrays of objects:
\\offers
[
{DeskUID: "B11A13", Day: 06 Jun 2020}
{DeskUID: "B11A13", Day: 07 Jun 2020}
{DeskUID: "B12B34", Day: 23 Jun 2020}
]
\\reservations
[
{DeskUID: "B11A13", Day: 06 Jun 2020, Name: "Mike"}
{DeskUID: "B12B34", Day: 23 Jun 2020, Name: "Ali"}
]
I would like to have a result where are available offers, that means only the offers without already reserved desks.
\\result
[
{DeskUID: "B11A13", Day: 07 Jun 2020}
]
How to get the difference between two arrays of objects in JavaScript
I already tried solutions on the link above but without success, I just got a result array as sum of all objects from the two arrays.
function comparer(otherArray){
return function(current){
var reserveDay = new Date (current.Day)
return otherArray.filter(function(other){
var offerDay = new Date (other.Day)
return other.DeskUID == current.DeskUID && offerDay == reserveDay
}).length == 0;
}
}
var onlyInA = offers.filter(comparer(reservations));
var onlyInB = reservations.filter(comparer(offers));
result = onlyInA.concat(onlyInB);
You can do in a single function, like:
const available = offers.filter(offer => {
return reservations.findIndex(reservation => reservation.DeskUID === offer.DeskUID && sameDay(new Date(reservation.Day), new Date(offer.Day))) === -1;
});
function sameDay(d1, d2) {
return (
d1.getFullYear() === d2.getFullYear() &&
d1.getMonth() === d2.getMonth() &&
d1.getDate() === d2.getDate()
);
}
console.log(available);
So, what is exactly happening...
offers.filter runs a check for each element in the offers array.
reservations.findIndex will try to find if there is already a reservation for that offer. It does by going into the reservations array and checking if there is the same unique ID and same date for the offer that is currently filtering.
If the result is equal to -1 it means that there is no reservation for that offer. Therefore it is available.
I've used the dates as string for the sake of simplicity, you can just alter to Date object. Hope it helps!
EDIT
I've added a small helper function for you to compare if the dates are from the same day. You can check this answer for a more detailed explanation: how to tell if two dates are in the same day?
You could take a Set and filter the reservations.
var getKey = ({ DeskUID, Day }) => [DeskUID, Day].join('|'),
offers = [{ DeskUID: "B11A13", Day: "2020-06-06" }, { DeskUID: "B11A13", Day: "2020-06-07" }, { DeskUID: "B12B34", Day: "2020-06-23" }],
reservations = [{ DeskUID: "B11A13", Day: "2020-06-06", Name: "Mike" }, { DeskUID: "B12B34", Day: "2020-06-23", Name: "Ali" }],
reservationsSet = new Set(reservations.map(getKey)),
open = offers.filter(o => !reservationsSet.has(getKey(o)));
console.log(open);
You can simply filter it with some inside it.
var offers = [{ DeskUID: "B11A13", Day: "2020-06-06" }, { DeskUID: "B11A13", Day: "2020-06-07" }, { DeskUID: "B12B34", Day: "2020-06-23" }];
var reservations = [{ DeskUID: "B11A13", Day: "2020-06-06", Name: "Mike" }, { DeskUID: "B12B34", Day: "2020-06-23", Name: "Ali" }];
var result = offers.filter(k=>!reservations.some(d=>d.DeskUID == k.DeskUID && d.Day==k.Day));
console.log(result);

Can't loop over grouped array in JS

Unfortunately I have again problems with my grouped messages. I have already received a lot of help from you, so I feel a bit embarrassed to ask again - but I'm not getting ahead with this.
My first goal was to group messages by their created_date. Thanks to your help this works very well now. Now I have tried to output the grouped messages, but it does not work. I slowly don't understand the whole thing anymore...
I want to output the group key (date) first and then every single message in each group. This is what I’ve tried:
jQuery(document).ready(function ($) {
let messages = [{
sender_id: "0",
message: "Test",
created_at: "Thu Mar 12 2020 17:26:23 GMT+0100 (Mitteleuropäische Normalzeit)"
}, {
sender_id: "0",
message: "Hallo",
created_at: "Thu Mar 12 2020 17:26:23 GMT+0100 (Mitteleuropäische Normalzeit)"
}];
let groupedMessages = [];
$(messages).each(function (index, message) {
let createdAtDate = new Date(message["created_at"]).toLocaleDateString(navigator.language, {
day: "2-digit",
month: "2-digit",
year: "numeric"
});
if (typeof groupedMessages[createdAtDate] === "undefined") {
groupedMessages[createdAtDate] = [];
}
groupedMessages[createdAtDate].push(message);
});
console.log(groupedMessages);
if (groupedMessages && groupedMessages.length > 0) {
$(groupedMessages).each(function (index, messages) {
console.log(index); //Expected output: 12.03.2020
$(messages).each(function (index, message) {
console.log(message["sender_id"]);
console.log(message["message"]);
console.log(message["created_at"]);
})
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
The issue is because of this line
let groupedMessages = []
You are setting groupedMessages as an array when it should be an object. array's don't have keys in JavaScript, which is why you get no output.
jQuery(document).ready(function ($) {
let messages = [{
sender_id: "0",
message: "Test",
created_at: "Thu Mar 12 2020 17:26:23 GMT+0100 (Mitteleuropäische Normalzeit)"
}, {
sender_id: "0",
message: "Hallo",
created_at: "Thu Mar 12 2020 17:26:23 GMT+0100 (Mitteleuropäische Normalzeit)"
}];
let groupedMessages = {};
$(messages).each(function (index, message) {
let createdAtDate = new Date(message["created_at"]).toLocaleDateString(navigator.language, {
day: "2-digit",
month: "2-digit",
year: "numeric"
});
if (typeof groupedMessages[createdAtDate] === "undefined") {
groupedMessages[createdAtDate] = [];
}
groupedMessages[createdAtDate].push(message);
});
console.log(groupedMessages);
if (groupedMessages && groupedMessages.length > 0) {
$(groupedMessages).each(function (index, messages) {
console.log(index); //Expected output: 12.03.2020
$(messages).each(function (index, message) {
console.log(message["sender_id"]);
console.log(message["message"]);
console.log(message["created_at"]);
})
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
To solve your problem I would suggest you such things:
You can`t use string keys for arrays, use objects in case you want to have string keys
Do not use $().each... because it iterates over jQuery collections, instead use built in js functions of $.each to iterate over js object or array.
Check solution below
jQuery(document).ready(function ($) {
let messages = [{
sender_id: "0",
message: "Test",
created_at: "Thu Mar 12 2020 17:26:23 GMT+0100 (Mitteleuropäische Normalzeit)"
}, {
sender_id: "0",
message: "Hallo",
created_at: "Thu Mar 12 2020 17:26:23 GMT+0100 (Mitteleuropäische Normalzeit)"
}];
let groupedMessages = {};
messages.forEach(function (message, index) {
let createdAtDate = new Date(message["created_at"]).toLocaleDateString(navigator.language, {
day: "2-digit",
month: "2-digit",
year: "numeric"
});
if (typeof groupedMessages[createdAtDate] === "undefined") {
groupedMessages[createdAtDate] = [];
}
groupedMessages[createdAtDate].push(message);
});
console.log(groupedMessages);
if (Object.keys(groupedMessages).length)
{
Object.keys(groupedMessages).map(date => {
console.log(date); //Expected output: 12.03.2020
groupedMessages[date].map(message => {
console.log(message["sender_id"]);
console.log(message["message"]);
console.log(message["created_at"]);
})
});
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Categories