Iterate over object and change values - javascript

I have a map of icons
const CategoryIconMap: Partial<
Record<Category, string>
> = {
[Category.BLUETOOTH]: mdiBluetooth,
[Category.BATTERY_MANAGEMENT_SYSTEM]:mdiBattery,
[Category.BATTERY_LOCK]: mdiLock,
[Category.DISPLAY]: mdiTablet,
};
but I want to make a new map that has the structure:
const CategoryIconMapWithTemplateResult: Partial<
Record<Category, TemplateResult>
> = {
[Category.BLUETOOTH]: html`<my-icon .path=${mdiBluetooth}></my-icon>`,
[Category.BATTERY_MANAGEMENT_SYSTEM]: html`<my-icon .path=${mdiBattery}></my-icon>`,
[Category.BATTERY_LOCK]: html`<my-icon .path=${mdiLock}></my-icon>`,
[Category.DISPLAY]: html`<my-icon .path=${mdiTablet}></my-icon>`,
};
I'd prefer to use a for..of or map over a forEach.
const CategoryIconMapToTemplateResult = Object.entries(CategoryIconMap).map(([key, value]) => CategoryIconMap[key] = html`<my-icon .path=${value}></my-icon>`);
but I'm getting an error Arrow function should not return assignment. Also, although it seems to work, I'm only getting the TemplateResult back, not the whole object.
I also tried using for..of
const CategoryIconMapToTemplateResult = () => {
for (
const [key, value] of Object.entries(CategoryIconMap)) {
CategoryIconMap[key] = html`<my-icon .path=${value}></my-icon>`;
}
}
But then everywhere else in my code complains that I am Placing a void expression inside another expression is forbidden. Move it to its own statement instead.
I also figured it out with reduce, but my company prefers fromEntries over reduce
Object.keys(CategoryIconMap).reduce((accumulator, [key, value]) =>
{
accumulator[key] = html`<my-icon .path=${value}></my-icon>`
return accumulator
}, {})
am I approaching this the right way?

Related

Firestore - Clever approach to converting all Firestore.Timestamp objects into Javascript date

I have a collection of objects that I want to retrieve. The objects have some date key:value pairs in them and I want to return all of those as a proper Javascript date. I don't want to declare them all one-by-one, as there are some dates that only exist on some objects, some I might not know about and in general, it's frustrating to declare everything one-by-one.
Here is my code that does not work, how could I get it working?
async function getChargesFromDatabase() {
const chargesCol = fsExpenses.collection('charges');
const chargesDocs = (await chargesCol.limit(50).orderBy('startTs', 'desc').get()).docs.map((doc) => {
const returnDoc: any = {};
for (const [key, value] of Object.entries(Object.entries(doc.data()))) {
returnDoc[key] = value?.toDate() ?? value;
}
return returnDoc;
});
return chargesDocs;
}
You will have to check all the keys as you are doing now by checking if a field is instance of Firestore Timestamp. Try using the following function:
const convertTimestamps = (obj: any) => {
if (obj instanceof firebase.firestore.Timestamp) {
return obj.toDate();
} else if (obj instanceof Object) {
// Check for arrays if needed
Object.keys(obj).forEach((key) => {
obj[key] = convertTimestamps(obj[key]);
});
}
return obj;
};
async function getChargesFromDatabase() {
const chargesCol = fsExpenses.collection('charges');
const chargesSnap = await chargesCol.limit(50).orderBy('startTs', 'desc').get()
const chargesDocs = chargesSnap.docs.map((doc) => convertTimestamps(doc.data()))
}

Loop through arguments passed to a method

I am trying to loop through an argument that is passed to a method and I am getting a TypeError: individualExpenses.map is not a function. What am I doing wrong here?
class ExpenseTracker {
constructor(payCheck, monthlyExpenses) {
this.payCheck = payCheck;
this.monthlyExpenses = monthlyExpenses;
}
storeExpenses(individualExpenses) {
let expenseStore = [];
individualExpenses.map(expense => {
expenseStore.push(expense)
})
console.log(expenseStore)
}
}
const v = new ExpenseTracker({}, {});
v.storeExpenses(1)
You are passing a numerical value to storeExpenses function and applying map over it. map works only on arrays. If you do
v.storeExpenses([1]);
it'll work just fine.
Alternatively, you can build logic to convert a non-array type to an array and use it in your storeExpenses function. This way you can do either of v.storeExpenses(1) or v.storeExpenses([1]) and the function will still work.
e.g.
const wrapToArray = (obj) => {
if (!obj) return [];
return Array.isArray(obj) ? obj : [obj];
};
and then modify your storeExpenses method as below -
storeExpenses(individualExpenses) {
let expenseStore = [];
wrapToArray(individualExpenses).map(expense => {
expenseStore.push(expense)
})
console.log(expenseStore)
}

Insert element inside array

I have a function
checkName(output) {
output.filter((NewData) => {
return this.props.elements.filter((OldData) => {
if (NewData.key == OldData.key) {
NewData.name = OldData.name,
//there i need to add another element
// Need to add newData.number = OldData.number
}
return NewData
})
})
return output
}
and I call this function like:
const named = this.checkName(product.rows)
Now I need to add to my product's array that I passed to checkName the value "OldData.Number" to "newData.Number" that is not defined in product (so I need to create this field)
For example:
Product before the checkName function
product.rows = [NewData.name]
Product after the checkName function
product.rows = [NewData.name="value of OldData.name", NewData.number="value of OldData.number"]
How can I obtain this result?
There are 2 confusing things in your code:
You are using filter to execute an action in each member of the output array. However, filter should be used to... well, filter that array, meaning that is should not modify it, just return a sub-set of it. Instead, you might want to use forEach. However, taking into accound the next bullet, probably you want to use map.
You are modifying the array passed to the checkName function. This is confusing and can lead to hard-to-find bugs. Instead, make your function "pure", meaning that it should not mutate its inputs, instead just return the data you need from it.
I would suggest some implementation like this one:
checkName(output){
return output.map((NewData) => {
// find the old data item corresponding to the current NewData
const OldData = this.props.elements.find(x => x.key === NewData.key);
if (OldData) {
// If found, return a clone of the new data with the old data name
// This uses the spread syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
return {
...NewData, // Clone the NewData object
name: OldData.name, // set the value found in OldData.name in the "name" field of the cloned object
number: OldData.number, // You can do the same for each field for which you want to replace the value cloned from NewValue
};
} else {
// Otherwise, just return a clone of the NewData
return { ...NewData };
}
}
}
The usage would be like this:
const named = this.checkName(product.rows)
Be aware that the product.rows array won't be modified!
You can get keys and values of the old object.
const keys = Object.keys(oldObject);
const values = Object.values(oldObject);
// or
const [keys, values] = Object.entries(oldObject);
After, you will create a loop with all keys of oldObject, and insert in newObject like a array.
keys.forEach( (key, index) => newObject[key] = values[index]);
// or
for (const [key, value] of Object.entries(object1)) {
newObject[key] = value
}
Use map like this.
checkName(output){
return output.map(( NewData) =>{
this.props.elements.forEach((OldData) => {
if (NewData.key == OldData.key) {
NewData.name = OldData.name;
NewData.number = OldData.number;
}
})
return NewData;
})
// return output;
}

Array reduce Unexpected use of comma operator no-sequences

I am getting an "Unexpected use of comma operator no-sequences" warning -- on the .reduce - but I am not sure how to resolve this.
const getQueryParams = () =>
this.props.location.search
.replace('?', '')
.split('&')
.reduce((r,e) => (r[e.split('=')[0]] = decodeURIComponent(e.split('=')[1]), r), {});
The code quoted uses (some would say abuses) the comma operator in order to avoid using the function body form of an arrow function. The minimal change to remove the comma operator is to put {} around the function body and do an explicit return:
const getQueryParams = () =>
this.props.location.search
.replace('?', '')
.split('&')
.reduce((r,e) => {
r[e.split('=')[0]] = decodeURIComponent(e.split('=')[1]);
return r;
}, {});
As a matter of style, though, I'd suggest not using reduce there at all. (I have a fair bit of company disliking reduce outside of Functional Programming with predefined, reusable reducers.)
In that code, the reduce is just a loop; the accumulator never changes, it's always the same object. So I'd just use a loop:
const getQueryParams = () => {
const result = {};
for (const e of this.props.location.search.replace("?", "").split("&")) {
result[e.split("=")[0]] = decodeURIComponent(e.split("=")[1]);
}
return result;
};
I'd probably also remove the redundant call to split:
const getQueryParams = () => {
const result = {};
for (const e of this.props.location.search.replace("?", "").split("&")) {
const [key, value] = e.split("=");
result[key] = decodeURIComponent(value);
}
return result;
};
Finally, both keys and values in query strings are URI-encoded, so decodeURIComponent should be used on both:
const getQueryParams = () => {
const result = {};
for (const e of this.props.location.search.replace("?", "").split("&")) {
const [key, value] = e.split("=");
result[decodeURIComponent(key)] = decodeURIComponent(value);
}
return result;
};
It'll work without if the keys are just alphanumerics and such, but it's not correct.
Stepping back from the syntax, though, you don't need to invent your own function for parsing query string parameters. Browsers already have one:
const getQueryParams = () => Object.fromEntries(
new URLSearchParams(this.props.location.search)
.entries()
);
Live Example:
const search = "?bar=Testing%201%202%203&baz=2";
console.log(
Object.fromEntries(
new URLSearchParams(search)
.entries()
)
);
You can rewrite the reduce call, so to avoid an assignment expression (and comma operator), turning the arrow function expression syntax into block syntax (see arrow function expression):
.reduce((r,e) => {
r[e.split('=')[0]] = decodeURIComponent(e.split('=')[1]);
return r;
}, {});
Another approach would be to use Object.assign:
let search = ["item=test","name=code%28","foo=%20bar"]
let result = search.reduce((r,e) =>
Object.assign(r,{[e.split('=')[0]] : decodeURIComponent(e.split('=')[1])}), {});
console.log(result)

Remove an object's key and value using a variable from function

Hey I'm trying to remove a key:value pair from state inside a Javascript Object.
It works when I hardcode the key name in the code, but when I try to use a variable from a function call, it does nothing.
Can somebody help me out?
Here's an object example:
toppingsSelected: {
"Onion":"true",
"Mushrooms":"true",
}
This works, hardcoded:
deleteTopping = toppingName => {
const { Onion, ...withoutOnion } = toppingsSelected;
console.log(withoutOnion); // Returns object without onion
};
This doesn't work:
deleteTopping = toppingName => {
const toppingName = "Onion"; // Variable gets passed in
const { toppingName, ...withoutOnion } = toppingsSelected;
console.log(withoutOnion); // Returns original object, no change made
};
So I'm basically trying to remove a key from React state but I'm pretty new to Javascript.
How can I make Javascript aware that toppingName is a key?
Another option is to add square brackets arround toppingName, and assign it to a variable. As #Bergi pointed out in the comments, this option does not mutate toppingsSelected
const toppingsSelected = {
"Onion":"true",
"Mushrooms":"true",
};
const toppingName = "Onion";
const {
[toppingName]: topping,
...withoutOnion
} = toppingsSelected;
console.log(JSON.stringify(withoutOnion));
To set the React state, you'd then do this
this.setState({ toppingsSelected: withoutOnion })
You can use delete e.g.
delete toppingsSelected[toppingName];
One way of doing this is using Array.prototype.filter()
const _obj = {
'Onion': true,
'notOnion': false
};
const newObj = Object.keys(_obj)
.filter(key => key !== 'Onion')
.reduce((acc, cur) => ({ ...acc, cur }), {})
console.log(newObj); // { notOnion: false }
This will return a new object without the 'Onion' property

Categories

Resources