Remove a property from array of object using reduce API - javascript

I am trying to delete a property name from the array of object, it's working properly using filter API,
const users = [
{ name: 'Tyler', age: 28},
{ name: 'Mikenzi', age: 26},
{ name: 'Blaine', age: 30 }
];
const myProp = users.filter(function (props) {
delete props.name;
return true;
});
console.table(myProp);
const myProp2 = users.reduce((people, user) => {
console.log(people);
console.log(user);
delete user.name;
return people;
}, []);
console.log(myProp2);
The same example before I am trying complete using reduce API, However, it's not working as expected.

It's not working because your not pushing to the previous element (you are always returning the empty array). You need to change it to:
const myProp2 = users.reduce((people, user) => {
delete user.name;
people.push(user)
return people;
}, []);
Please note that is not the intended use for reduce though - map is the operation you are looking for:
const myProp2 = users.map(u=> ({age: u.age}));

You actually want to use map for this, because you are selecting a transormation of the data into a new object (similar to Select in SQL or LINQ)
const myProps = users.map(u=> ({age: u.age}))
Also although the filter method worked, this is actually abuse of the filter method. The filter method is supposed to remove elements from the array depending on a condition. Your method worked because you returned true (which removed no elements) but you modified the current value on each iteration.
This is bad practice because you will confuse the next person to look at your code, they will wonder why you used filter as a method to transform the data rather than map.
Also don't use reduce because reduce is an aggregation function intended to perform aggregate functions on objects. Since the number of elements you are returning will be the same, map is better for this.
Reduce would be better suited for if you wanted to find out the average,max,min,median age or the most popular name etc...

Related

VueX Mutation Optimization

I am working with a VueX store at the moment, and I have 1 mutation that does a push to entire array. Everything works currently, but want to know if I can make this code simpler, less lines / more optimized at the same time.
Side note, these are objects stored in an array.
PUSH_TO_ALL: (state, data) => {
const found = state.all.find((one, i) => {
if (one.slug === data.slug) {
state.all[i] = data // modify current state
return true
}
return false
})
if (!found) {
state.all.push(data) // add to the state
}
}
My first thought is that if you compare the slug in data to that in every object in the array, it must be unique (otherwise you would replace multiple objects in the find).
This means that you can almost certainly make things a lot faster and a lot simpler if you switch from having the 'root' of state be an array, to using an object instead, indexed by slug.
Then your code would switch to being something like:
PUSH_TO_ALL: (state, data) => {
state.all[data.slug] = data
This has 2 advantages - it is much simpler and faster to modify state, since you don't need to walk all of state checking if the object already exists. And secondly there's no need for separate code to distinguish between adding a new object, and replacing it if it already exists.
If for some reason you have to store state as an array, I would use a different part of state to maintain an object which tracks slug to array index. Then your code would be something like:
PUSH_TO_ALL: (state, data) => {
if (state.map[data.slug]) {
state.all[state.map[data.slug]] = data
} else {
// Push returns length of array - index is len-1
state.map[data.slug] = state.all.push(data) - 1
}
Note - in Vue2 you may need to use Vue.set() to update nested objects, since otherwise the code may not react to these changes. Vue3 no longer has this limitation.
You could use Array.findIndex instead of Array.find, and pair it with a ternary to make the trivial logic more concise (though not necessarily clearer).
const mutations = {
PUSH_TO_ALL: (state, data) => {
const indexOfMatchingSlug = state.all.findIndex(one => one.slug === data.slug);
state.all[indexOfMatchingSlug] = data ? indexOfMatchingSlug > -1 : state.all.push(data);
}
}
Array.findIndex documentation
JavaScript ternary operator documentation

RxJs simplify repeated pluck + flatMap

Starting from an object like this:
const business = {
id: "1a2b3c",
accounts: [
{
name: "Pizza Express",
stores: [
{ id: "5", webSite: "www.pizza-ny.com" },
{ id: "6", webSite: "www.pizza-la.com" }
]
}
]
};
Expected output:
A stream of Stores (not an array of Stores)
My attempt:
return business.pipe(
pluck("accounts"),
flatMap(val => val),
pluck("stores"),
flatMap(val => val)
);
This works but, for me, the operators seem like repetitive and I'm wondering if I can simplify them. Also tried with the common map but I can't get it.
For example:
return business.pipe(
pluck("accounts"),
map(val => val.stores)
);
I don't understand why this returns undefined. I get an array of accounts and then projecting the stores property of each one... I guess I'm misunderstanding something.
Do you see a simpler or more elegant approach?
There will be some duplication in your operators since you are reaching into nested objects for nested arrays. There is probably a way to write a nice custom operator (maybe I'll look into that if I have time), but I think the simplest way is to use just the mergeMap (same thing as flatMap).
It will look something like this (edited to filter out accounts without stores – credit to Picci's comment)
import { of } from "rxjs";
import { mergeMap, filter } from "rxjs/operators";
of(business)
.pipe(
mergeMap(b => b.accounts),
/* filter out accounts that don't have stores */
filter(a => !!a.stores),
mergeMap(a => a.stores)
)
.subscribe(console.log);
/** console output:
*
* { id: "5", webSite: "www.pizza-ny.com" }
* { id: "6", webSite: "www.pizza-la.com" }
*/
Here is the stackblitz: https://stackblitz.com/edit/rxjs-njvj7q?devtoolsheight=33&file=index.ts
Side note: you could even use switchMap() instead of mergeMap(). Since you are dealing with observables that complete, there isn't much difference between which one you use.
EDIT (explanation for map differences)
The reason your map() isn't working is because you are trying to access .stores on an array, not an object:
return business.pipe(
pluck("accounts"), // accounts is an array
map(val => val.stores) // `accounts[0].stores` would return a value for you
);
map works differently between Array.prototype.map() and RxJS map() because RxJS has a different implementation.
In plain JavaScript map() is a function on an array and will return a new array. The key there is it is always array based. From the docs (emphasizes added):
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
In RxJS map() is an operator that is applied to any value inside an Observable. It is not guaranteed to be an array so it does not iterate over the value. RxJS map() is taking the value inside of an Observable and "mapping" it to a new structure/value. From the docs (emphasizes added):
Applies a given project function to each value emitted by the source Observable, and emits the resulting values as an Observable.
If I understand right, what you want to achieve is to create an Observable that emits, as a stream, each store of all the accounts.
So one way could be this
business.pipe( // business is assumed to be an Observable that emits the business Object you describe in your question
map(b => b.accounts),
mergeMap((accounts) => accounts),
mergeMap(account => account.stores)
)
At the end of the day it is not much different from your solution.
The reason why your last snippet does not work is explained in the comments I have added to your code below.
return business.pipe(
// With `pluck(accounts)` you extract the `accounts` property of the `business` object.
// Such property is an `array` of accounts.
pluck("accounts"),
// Now you want to map the `stores` property of `val`, but `val` is an array
// and an array has not `stores` property, so you get `undefined`
map(val => val.stores)
);

Access objects own key-value that is within an array

I know variations of this question have been asked but bear with me.
I have the following array containing objects (which are routines):
const PREMADE_ROUTINES = [
{
itemIds: ['makebed', 'drinkwater', 'quickstretch', 'hotshower', 'brushteeth', 'smallsnack', 'getdressed', 'todolist', 'declutterinbox',],
routineDuration: DEFAULT_ROUTINE_ITEMS.getItemsFromIds(PASS THIS OBJECTS itemIds HERE)
}
]
How could I access the itemIds in this case within each of the objects in the PREMADE_ROUTINES array?
In this case, I would need to pass the objects itemIds as an argument to the function. I could do this if the object wasn't in an array with a get(). However, I don't know how to do it in this case. Any ideas?
Ideally, I would like to simply access the routineDuration key by looping and simply accessing it.
In your particular context the best solution is to break away the itemIds declaration. This is so you don't run into this issues when populating it later on.
const DEFAULT_ROUTINES = [
...
]
const PREMADE_ROUTINES = [ {
itemIds: DEFAULT_ROUTINES,
routineDuration: yourFunctionHere(DEFAULT_ROUTINES)
]}
I notice your data structure is a bit complex. It might be worth refactoring it, and introducing a couple utility / filtering methods to make your life easier in the future.
You could create a method returning your routines object. That way it'd be more reusable.
const createRoutine = (routines) => ({
itemIds: routines,
routineDurations: getRoutineDurations(routines)
});
const PREMADE_ROUTINES = [
createRoutine(['makebed', 'drinkwater', 'quickstretch']),
createRoutine(['hotshower', 'brushteeth', 'smallsnack']),
];

Convert Array's into Individual Values in .forEach()

working on a data transformation project that is taking queries from five different databases, and merge them together. (There are two record sets that are very similar to each other and another set of two that are similar to each other.)
Three of five returned records are fine. The other two (which are similar) are oddly returning certain fields as arrays instead of just single values.
i.e.:
dbRecords = [
{
FirstName: ['john', 'john doe']
}
]
it's definitely due to poor data maintenance, but I want to convert these to single values and I was thinking I could do it something like this.
dbRecords.forEach((item, index, arr) => {
Object.keys(item).forEach(i => {
if(i instanceof Array){
item = item[0];
}
}
});
Would that do the trick?
Would that do the trick?
No, cause item is the object and not the value you want to change, you would have to do:
item[i] = item[i][0];
And additionally, i is always a string and never an Array the check must be:
if(item[i] instanceof Array){
And then you would have to store it back to the db.
PS: i is a bad variable name, why dont you just take key or something similar?
"Three of five returned records are fine. The other two (which are similar) are oddly returning certain fields as arrays instead of just single values."
From that statement, it sounds like you know precisely which two fields are the unexpected arrays. In that case, you shouldn't use a loop, but rather target those fields directly.
So assuming the fields are FirstName and LastName, you can do it like this:
dbRecords = dbRecords.map(({FirstName:[FirstName], LastName:[LastName], ...rest}) =>
({FirstName, LastName, ...rest})
);
This takes advantage of parameter destructuring and "rest sytnax" in object literals to extract the first array member two fields that interest you, as well as the rest of the fields into a separate object, then returns a new object with the extracted values along with the rest of the fields.
Here's a working example:
let dbRecords = [{
FirstName: ['john', 'john doe'],
LastName: ['doe', 'john doe'],
OtherField: "foobar"
}, {
FirstName: ['bob', 'bob smith'],
LastName: ['smith', 'bob smith'],
OtherField: "raboof"
}];
dbRecords = dbRecords.map(({FirstName:[FirstName], LastName:[LastName], ...rest}) =>
({FirstName, LastName, ...rest})
);
console.log(dbRecords);

Redux-persist react native AsyncStorage Object Serialization Objects Not Equal

I am using redux persist to automatically persist and rehydrate the state on application launch as described in the docs, specifically using AsyncStorage: https://github.com/rt2zz/redux-persist.
I have a reducer defined below which keeps the current products added to the shopping cart in state.products
case 'ADD_PRODUCT':
let addedProducts = [...state.products]
addedProducts.push(action.product);
return {
...state,
count: ++state.count,
products: addedProducts
};
case 'REMOVE_PRODUCT':
let count = state.count;
let removedProducts = [...state.products];
let idxOfProduct = state.products.indexOf(action.product);
if(idxOfProduct != -1){
count = --state.count;
removedProducts.splice(idxOfProduct,1);
}
return{
...state,
count: count,
products: removedProducts
};
#1. If I dispatch 'ADD_PRODUCT', it adds the product and then if I dispatch 'REMOVE_PRODUCT' it removes the item as expected.
#2.1 If I dispatch ADD_PRODUCT and then RELOAD my app, the state.products is rehydrated as expected and contains the recently added product.
#2.1.However attempt to call REMOVE_PRODUCT (exactly the same way I called REMOVE_PRODUCT in #1 above) after a I have RELOAD the app. Even though state.products contains the product state.products.indexOf(action.product); returns back -1 and as a result it is not removed.
Why does IndexOf method in #1 work correct as expected when REMOVE_PRODUCT is called. However if I add a product(ADD_PRODUCT) then reload my app and call REMOVE_PRODUCT, IndexOf returns -1 even though it is present in the state.products
I think the problem may be related to the way indexOf treats object equality.
Without reloading, you are adding and removing the same object reference, which is OK.
When you reload, the reference loaded in state.products is different from the one in action.product, so indexOf cannot find it and never returns the index.
To fix this I would use the product id to find that product in the state.products array instead of trying to find the whole object.
To illustrate a bit my answer, this is what you are doing:
var a = {obj: 0};
var b = [a];
b.indexOf({obj: 0}); // -1 not found
This is what you should do:
var a = {id: '26833', obj: 0};
var b = [a];
b.findIndex(function(el){ //findIndex is not supported in IE, find a polyfill for it
return el.id === '26833'
}); //0
This happens, because indexOf uses a strict reference equality check to find the element within the array. This means that it's not enough for the objects to have the same fields and values: it needs to be the very same object. After the app has been reloaded, this can never be true, since the original object has been destroyed.
If your products have some sort of unique ID field, the easiest way to do this would be to filter the list to exclude the item with a matching id:
const products = state.products.filter(p => p.id !== action.product.id);
const count = products.length;
return { ...state, products, count };

Categories