add a field and update another in array of objects - javascript

A query returns an array of objects from a collection. I want to add a field to each of the objects in that array, and update another field in every object of the array.
Products array before update:
[{ _id: 58d895b8ffc0346230a43a89,
event: 'Junior Girls 12s',
group: 'nonpro',
price: 50,
day: 'Friday' },
{ _id: 59d895b8ffc0346230a43a89,
event: 'Junior Girls 14s',
group: 'nonpro',
price: 50,
day: 'Friday', }]
My code to update the array of objects:
//add the field and changed price if late fee applies
for(var i = 0; i < products.length; i++) {
products[i].field = registered.frifield;
console.log(products[i].field);
if(settings.latefeeamt > 0 && settings.frilatefee === true) {
products[i].price += settings.latefeeamt;
}
console.log(products[i]);
console.log(events.friday);
}
How products array SHOULD look after update:
[{ _id: 58d895b8ffc0346230a43a89,
event: 'Junior Girls 12s',
group: 'nonpro',
price: 60,
day: 'Friday',
field: 'Main' },
{ _id: 59d895b8ffc0346230a43a89,
event: 'Junior Girls 14s',
group: 'nonpro',
price: 60,
day: 'Friday',
field: 'Main' }]
How can I get this to work? It console.logs the correct field inside the loop, but I get the original array when it's done.

As a rule, Don't change the array you are iterating, it's bad practice.
You can use map : array.map
var array = products.map((element, index) => {
element.field = registered.frifield;
if(settings.latefeeamt > 0 && settings.frilatefee === true) {
element.price += settings.latefeeamt;
}
return element
});
This should return an array, inside you will have the updated product list

Hi there i wasn't sure about this portion of code because settings isn't defined
if(settings.latefeeamt > 0 && settings.frilatefee === true) {
products[i].price += settings.latefeeamt;
}
Here you have a code that works i change the condition depending on the day, you can update it depending on your specification hope it helps
var products = [{
_id: '58d895b8ffc0346230a43a89',
event: 'Junior Girls 12s',
group: 'nonpro',
price: 50,
day: 'Friday'
},
{
_id: '58d895b8ffc0346230a43a89',
event: 'Junior Girls 14s',
group: 'nonpro',
price: 50,
day: 'moday',
}];
console.log(products);
for(product of products) {
product.field = 'Main';
if (product.day === 'moday') {
product.price = 30;
}
}
console.log(products);

Related

Apply Combo Discount to a Food Order

An app lets users order food from a menu. The menu has three types of selection: main, drink and dessert. A feature needs to be added which will discount the price by 10% for every main+drink combo (10% off every combo). All items ordered by the customer are stored in an array like so:
order = [
{id: 4, count: 1, type: "main", price: 10}
{id: 5, count: 2, type: "drink", price: 9.5}
]
As you can see, each item the customer orders has a count property. How can I apply the discount without mutating the order array or any of the object properties? Ideally I'd like to loop through the array, determine total number of combos (in the example above it would be 1), determine the total discount value and pass that value to another function which computes the order total. If anyone can suggest a better way of doing it, I'm all ears (or eyes in this case).
Also, what is the best way to express this problem from a technical point of view?
const userOrder = [
{ id: 4, count: 1, type: "main", price: 200 },
{ id: 5, count: 1, type: "drink", price: 100 }
];
const orderInfo = userOrder.reduce((acc, cur) => {
console.log('cur', cur)
if (acc[cur.type]) {
return {
...acc,
[cur.type]: cur.count,
totalAmount: (cur.count * acc.totalAmount)
}
} else {
return {
...acc,
[cur.type]: cur.count,
totalAmount: (cur.count * cur.price ) + acc.totalAmount
}
}
}, {
main: 0,
drink: 0,
totalAmount: 0
});
const noOfComobosPresent = Math.min(orderInfo.main, orderInfo.drink);
const totalDiscountValue = noOfComobosPresent * 10;
const finalAmount = orderInfo.totalAmount - ((orderInfo.totalAmount * totalDiscountValue ) / 100) ;
console.log('finalAmount', finalAmount)

Most efficient way to add unique ids to deeply nested array of objects

I need to add ids to an array of objects:
Example:
const workouts = [
{
type: "walk"
areas: [
{
location: 'park'
day: 'Sunday'
},
{
location: 'beach'
day: 'Monday'
}
],
exercise: true
},
{
type: "run"
areas: [
{
location: 'gym'
day: 'Saturday'
},
{
location: 'track'
day: 'Sunday'
}
],
exercise: true
}
]
In this example, I need to add ids to the workouts array and the deeply nested areas array.
I came up with this solution mapping through each of the arrays:
const idMappings = workouts.map((workout) => {
const workoutIdMapping = workout.id ? workout : { ...workout, id: uuid.v4() }; // if there is already an id just return the object -- otherwise generate a random id
const areasIdMapping = workoutIdMapping.areas.map((area) => {
return area.id ? area : { ...area, id: uuid.v4() }; // if there is already an id just return the object -- otherwise generate a random id
});
return { ...workoutIdMapping, areas: areasIdMapping }; // finally return the updated values with ids
});
This correctly returns me the following area with ids:
const workouts = [
{
id: "0000-0000-4e6b-9f24-f8e1f2402a9b"
type: "walk"
areas: [
{
id: "0000-1111-4e6b-9f24-f8e1f2402a9b"
location: 'park'
day: 'Sunday'
},
{
id: "0000-2222-4e6b-9f24-f8e1f2402a9b"
location: 'beach'
day: 'Monday'
}
],
exercise: true
},
{
id: "1111-0000-4e6b-9f24-f8e1f2402a9b"
type: "run"
areas: [
{
id: "1111-1111-4e6b-9f24-f8e1f2402a9b"
location: 'gym'
day: 'Saturday'
},
{
id: "1111-2222-4e6b-9f24-f8e1f2402a9b"
location: 'track'
day: 'Sunday'
}
],
exercise: true
}
]
This algorithm worked correctly but is there a more efficient way to solve this problem -- Is mapping multiple times not an efficient way of approaching this problem -- perhaps there are better ES6 functions I can use?
Maybe you could use recursion here. Like this:
function setIds (arr) {
arr.forEach(item => {
item.id = uuid.v4()
if (item.areas && item.areas.length > 0) {
setIds(item.areas)
}
})
}
setIds(workouts)

Group by list of dictionary in javascript

Input =
[{id: 13, display_name: "Customizable Desk (Aluminium, Black)", quantity: 4, unit_price: "800.40", discount: 0, price: "3201.60"},
{id: 40, display_name: "Magnetic Board", quantity: 2, unit_price: "1.98", discount: 0, price: "3.96"},
{id: 40, display_name: "Magnetic Board", quantity: 1, unit_price: "1.98", discount: 0, price: "1.98"},
{id: 40, display_name: "Magnetic Board", quantity: 1, unit_price: "1.98", discount: 0, price: "1.98"}]
Output =
[{id: 13, display_name: "Customizable Desk (Aluminium, Black)", quantity: 4, unit_price: "800.40", discount: 0, price: "3201.60"},
{id: 40, display_name: "Magnetic Board", quantity: 4, unit_price: "1.98", discount: 0, price: "7.92"}]
I am able to achieve an answer but my process is very lengthy, I need a short answer for it using some predefined functions of javascript.
Here's a short way to do this (based on the assumption I cited before that quantity is the only thing that can vary for each item with the same id value):
inputArray.reduce((result, item) => {
if (result.map.has(item.id)) {
result.map.get(item.id).quantity += item.quantity;
} else {
result.array.push(item);
result.map.set(item.id, item);
}
return result;
}, {map: new Map(), array: []}).array
This uses the array reduce function, if you're not familiar with that. This could be done without the Map, but that makes it more efficient than searching through the whole result array to find id values which have already been seen.
The idea behind this code is that you keep the first item you see that has an id that you haven't seen before, and if you have seen an id before, you look up that original item, and add the new quantity to the previous quantity.
I'd do something like this:
function groupProducts( input ) {
var productsById = input.reduce( function( current, item ) {
if( !current[ item.id ] ) {
current[ item.id ] = [];
}
current[ item.id ].push( item );
return current;
}, {} );
return Object.keys( productsById ).map( function( id ) {
productsById[ id ].reduce( function( current, item ) {
if( current ) {
// this could be extracted as a closure passed in to coalesce items together. Your rules for how that happens go here.
current.quantity += item.quantity;
current.price += item.price;
return current;
} else {
return Object.assign( {}, item ); // shallow copy beware
}
}, null );
} );
}
PS I noticed in your input sample things like quantity and price were strings instead of numbers. I'm going to assume you know how to straighten those things out so the data has proper data types. This won't work if you have strings for those.

How to create a json grouped by date in js?

I have an array of objects sorted by date:
const alerts = [{
id: 1, date: '2018-10-31T23:18:31.000Z', name: 'Joke', title: 'this is the first 1'
}, {
id: 2, date: '2018-10-30T23:18:31.000Z', name: 'Mark', title: 'this is the second one'
}]
I am trying to 'group' the alerts by date so trying to create 'datesections' which have a dateheader, the result should be something like:
const sections = [{
date: '2018-10-31T23:18:31.000Z',
heading: 'today',
alerts: [{ id: 1, date: '2018-10-31T23:18:31.000Z', name: 'Joke',
title: 'this is the first one' }]
}, {
date: '2018-10-30T23:18:31.000Z',
heading: 'Yesterday',
alerts: [{ id: 2, date: '2018-05-30T23:18:31.000Z', name: 'Mark',
title: 'this is the second one' }]
}]
I tried something this but can't figure out how to get the alerts with the same date in the alerts prop:
const sections2=alerts.map(a =>
({
date: a.date,
heading:'today new',
alerts:alerts
})
)
const alerts = [
{ id: 1, date: '2018-10-31T23:18:31.000Z', name: 'Joke', title: 'this is the first 1' },
{ id: 2, date: '2018-05-30T23:18:31.000Z', name: 'Mark', title: 'this is the second one' }
]
const grouping = _.groupBy(alerts, element => element.date.substring(0, 10))
const sections = _.map(grouping, (items, date) => ({
date: date,
alerts: items
}));
console.log(sections);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Can't help you with headings - what if it's neither "today" or "yesterday"?
I feel like you are asking a couple of things here. The key one is how to group by day with a date.
To do that you will first need to know how to group. This answer may help with that.
As far as how to group by day there are a number of ways to do that. Simplest I can think of is to cut off everything after the "T" in the date string and sort that.
From my point of view it's not really a map what you need here, map will return a new array but not what you want. You can do this with 2 for statements
let total = [];
for (let j = 0; j < alerts.length; j++) {
let item = alerts[j];
let foundDate = false;
for (let i = 0; i < total.length; i++) {
if (total[i].date === item.date) {
foundDate = true;
total.alerts.push(item);
}
}
if (!foundDate) {
console.log("!found");
total.push({
date: item.date,
heading: "Yesterday",
alerts: [item]
});
}
}
If you console.log yout total array, will contain what you want.
If you need any other explanation pls let me know.
You can use a regular expression to match the part of the date you want and then group your data. You can add there the header you want. Hope this helps.
const alerts = [
{ id: 1, date: '2018-10-31T23:18:31.000Z', name: 'Joke', title: 'this is the first 1' },
{ id: 2, date: '2018-10-30T23:18:31.000Z', name: 'Mark', title: 'this is the second one' },
{ id: 3, date: '2018-10-30T23:14:32.000Z', name: 'Mark', title: 'this is the third one' }
];
const groupByDate = (data) => {
return data.reduce((acc, val) => {
const date = val.date.match(/\d{4}-\d{2}-\d{2}/g).toString();
const item = acc.find((item) => item.date.match(new RegExp(date, 'g')));
if (!item) acc.push({ date: val.date, alerts: [val], heading: 'some heading' });
else item.alerts.push(val);
return acc;
}, []);
};
console.log(groupByDate(alerts));
Maybe you need something like this? Didn't have much time for this and last array parsing might be done in more elegant way ;)
var alerts = [
{ id: 1, date: '2018-10-31T23:18:31.000Z', name: 'Joke', title: 'this is the first 1' },
{ id: 3, date: '2018-10-31T23:44:31.000Z', name: 'Joke1', title: 'this is the 2nd' },
{ id: 2, date: '2018-10-30T23:18:31.000Z', name: 'Mark', title: 'this is the second one' },
{ id: 4, date: '2018-10-30T23:45:31.000Z', name: 'Mark1', title: 'this is the 3rd' },
{ id: 5, date: '2018-10-27T23:18:31.000Z', name: 'Mark2', title: 'this is the 4th' },
];
var processedAlerts = [], finalAlerts;
(function(initAlerts){
//iterate through array to make keys to group by day
for(var i = 0; i < initAlerts.length; i++){
processedAlerts[i] = initAlerts[i];
//substring here can be more sophisticated - this was faster
initAlerts[i].keyDate = initAlerts[i].date.substr(0, 10);
}
//supporting function to convert string to date
//to acheve more detailed sorting that includes time
//just use date object and use hours, minutes and/or seconds to create Date object
function dateFromString(strDate){
var date, tmpDate;
//convert string to array - I assume that date format is always the same
//yyyy-mm-dd and will become Array 0: year, 1: month, 2: day of the month
tmpDate = strDate.split("-");
//moths in js are zero pased so Jan is 0, Feb is 1 and so on
//so we want to substract 1 from human readable month value to get correct date
date = new Date(tmpDate[0], tmpDate[1]-1, tmpDate[2]);
return date;
}
//function used to compare dates and passed to sort function
function comparedates(obj1, obj2){
var date1, date2;
date1 = dateFromString(obj1.keyDate);
date2 = dateFromString(obj2.keyDate);
let comparison = 0;
if(date1>date2){
comparison = 1;
} else if(date1<date2){
comparison = -1;
}
//to achieve reverse just multiply comparison result by -1
return comparison*(-1);
}
function getHeader(date){
//here place logic to generate header
//this involves comparing dates probably from keyDate
return "temp header: " + date.toString()
}
//sort the array by keyDate from newest to oldest
processedAlerts.sort(comparedates);
//final array rebuild
//pass here sorted array
finalAlerts = (function(arrayAlerts){
var aAlerts = [], k = 0;
for(var j = 0; j < arrayAlerts.length; j++){
//check if entry for date exists
//if no than create it
if(!aAlerts[k]){
aAlerts[k] = {
//removed title because I asummed that each alert has unique title and put them in alerts instead
date: arrayAlerts[j].keyDate, //agroupped date
heading: getHeader(arrayAlerts[j].keyDate), //update this function to return personalized heading
//here you can shape the alert object how you need
//I just passed it as it was
alerts: [arrayAlerts[j]] //array with first object inside
};
} else {
//add another alert to day
aAlerts[k].alerts.push(arrayAlerts[j]) //array with first object inside
}
//increasing final array key
//if there is previous entry and keys are the same for current and previous
if(arrayAlerts[j-1] && (arrayAlerts[j].keyDate == arrayAlerts[j-1].keyDate)){
k++;
}
}
return aAlerts;
})(processedAlerts);
})(alerts);
console.log(finalAlerts);

Converting Array to Object for selecting objects (Refactoring / Optimizing)

While I was facing slow loading time when it iterate array to render objects, I want to change its data structure. I show table of contents for seasons. When user clicks an item, the item is marked as selected.
Here is current data structure (Array)
const seasons = [{
id: 6,
value: 'All',
}, {
id: 7,
value: 'Spring',
}, {
id: 8,
value: 'Summer',
}, {
id: 9,
value: 'Fall',
}, {
id: 10,
value: 'Winter',
}];
I'm storing selected Season Ids as an Array now
state = {selectedSeasonIds: []}
When selectedSeasonIds has id, I want to remove the id from it. Otherwise, add the id to selectedSeasonIds. (This is current approach)
if(_.includes(this.state.selectedSeasonIds, id)) {
let newSelectedSeasonIds = _.filter(this.state.selectedSeasonIds, (curObject) => {
return curObject !== id;
});
this.setState({selectedSeasonIds : newSelectedSeasonIds});
} else {
let newSelectedSeasonIds = [...this.state.selectedSeasonIds, id];
this.setState({selectedSeasonIds : newSelectedSeasonIds});
}
And here is my pseudo-code for refactoring to convert my arrays to object structure for performance. (I found searching on an object is MUCH faster than searching on the array)
Changing the array to object
const seasons = {
6 :{
id: 6,
value: 'All',
},
7: {
id: 7,
value: 'Spring',
},
8: {
id: 8,
value: 'Summer',
},
9: {
id: 9,
value: 'Fall',
},
10: {
id: 10,
value: 'Winter',
}
};
Changing Selected Seasons <- I want to store only the key(id) of the objects. But I want to use it as an object
state = {selectedSeasonIds : {}} Can I store object type state?
Here is expected logic which can be 50 times faster than array search.
if(selectedSeasonIds[id]) {
//remove
return _.omit(state.selectedSeasonIds, id); < is this right?
} else {
//add
return {...state.selectedSeasonIds, [id]:id} <- Does this look ok?
}
Well if you think this is right, you can copy and paste my code to the answer (I will edit my question too).
Otherwise, Can you provide better suggestion or find the error?
Thank you so much
I guess you have to loop through seasons in order to render them.
My first suggestion is to add selected prop in each one of them so you don't have to check in selectedSeasonsIds on every render.
In case this is not an option, you can still keep the key value approach.
onAdd(id) {
this.setState({
selectedSeasonsIds: {
...this.state.selectedSeasonsIds,
[id]: this.state.selectedSeasonsIds[id] ? false : true
}
})
}
When checking for specific season whether they are selected or not, simply:
render() {
const { seasons, selectedSeasonsIds } = this.state
return (
<div>
...
{Object.keys(seasons).map(key =>
<ItemComponent
{...propsThatYouMightNeed}
selected={selectedSeasonsIds[key]}
/>
)}
</div>
)
}
Maybe something like this? I'd recommend storing arrays and then converting as necessary for lookups.
const seasons = [{
id: 6,
value: 'All',
}, {
id: 7,
value: 'Spring',
}, {
id: 8,
value: 'Summer',
}, {
id: 9,
value: 'Fall',
}, {
id: 10,
value: 'Winter',
}];
const seasonsHash = _.keyBy(seasons, 'id');
// check for existence
const hasId = _.has(seasonsHash, id)
// remove and convert back to array
_.values(_.omit(seasonsHash, id))
// add new id
_.concat(_.values(seasonsHash), id)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Categories

Resources