How To concat an Object and Array in Javascript? - javascript

I have a user with a favorites array
I am trying to concat an item to the favorites field.
Here I assign the user to userToChange variable
const userToChange = action.data.user;
And here I concat a 'card to the user's favorites field
const changedUserFavorites = userToChange.favorites.concat(action.data.card);
I need to pass the updated user field to an axios update call. I have tried to concat the user and the updated favorites field like this.
const changedUser = userToChange.concat(changedUserFavorites);
but I am getting an error: ×
Unhandled Rejection (TypeError): userToChange.concat is not a function
I suspect this might be due to the fact that userToChange is an object
Here is what userToChange looks like:
User to Change
Object
favorites: [Array(1)]
firstName: "phil"
id: "5de9930fedc70846747305f6"
token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InBoaWwiLCJmaXJzdE5hbWUiOiJwaGlsIiwiaWF0IjoxNTc1NTkwNDkwfQ.SYoApD4FUuXDEhjWSDBg0_gkKmf7a2FHm5Yifu2yhgw"
username: "phil"
__proto__: Object
and changedUserFavorites is an Array.
Thus, I just wonder how can I put the two together given that one field is an object?

For objects, you can use Object.assign to append properties onto an existing object.
let myObj = { a: 1 }
let newObj = { b: 1 }
Object.assign(myObj, newObj)
///myObj = { a: 1, b:1 }
For arrays, you have the Array.prototype.concat method.
let myArr = [1, 2]
myArr.concat([3, 4])
//myArr = [1, 2, 3, 4]
MDN
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat

Related

How to iterate through enums with integer as value in Typescript?

I have an enum
export enums Actions {
All = 1,
Success = 2,
Failed = 3
}
When I iterate through it using for loop, I am getting total of 6 entries. I get to know that this is how it works in Typescript. But how can I use the enum so that I can access
enum.key
for "All, Success, Falied" and
enum.value
for 1,2,3
As per the docs of typescript, if as const keyword suffices, then we dont need to use enum.
Just interpreting, maybe in this case an implementation like illustrated can be used, with Object.keys(obj) and Object.values(obj) to get the required outputs
const Actions = {
All: 0,
Success: 1,
Failure: 2
} as const
let keyArr = Object.keys(Actions);
let valArr = Object.values(Actions);
What you can do in order to access the enum keys and values like you described in the OP is convert your enum to a basic object that has the properties keys and values with the corresponding data.
export enum Actions {
All = 1,
Success = 2,
Failed = 3
}
export type ConvertedActions = {
keys: string[];
values: Actions[];
};
const result = Object.values(Actions).reduce(
(acc, curr): ConvertedActions =>
isNaN(+curr)
? {...acc, keys: [...acc.keys, curr as string]}
: {...acc, values: [...acc.values, curr as Actions]},
<ConvertedActions>{keys: [], values: []}
);
console.log(result.keys); // [ 'All', 'Success', 'Failed' ]
console.log(result.values); // [ 1, 2, 3 ]
Note: Don't get confused by the extra type (ConvertedActions) I defined. However, it is needed to avoid using any a lot.
const keys = Object.keys(Actions);
const values = Object.values(Actions);
What you can do, in this specific case, is separating All, Success, Falied and 1, 2, 3 in two different arrays.
export enums Actions {
All = 1,
Success = 2,
Failed = 3
}
console.log(Object.keys(enums).filter((v) => isNaN(Number(v)))); // ["All", "Success", "Failed"]
console.log(Object.keys(enums).filter((v) => !isNaN(Number(v)))); // ["1", "2", "3"]
You can also do it with a for..in loop:
for (const value in enums) {
console.log(value); // 1, 2, 3, All, Success, Falied
console.log(value.filter((v) => isNaN(Number(v)))); // All, Success, Falied
console.log(value.filter((v) => !isNaN(Number(v)))); // 1, 2, 3
}
There are more ways to do it using forEach, for..of, etc. But from your question I think my example should do the trick.

Update object with new changes that don't override unchanged values Vue.js 2

I'm having an issue where a payload I'm receiving is not updating the values correctly for an object before passing it to the database. In other words, the new changes are not persisting for fields have new or changed values. I'm using Vue.js 2. How do I successfully update the incoming object and storing those values in an existing object with changes?
More information: We receive an object from an API that may have existing keys with values or none at all if the meeting matches certain characteristics - like the username/birthday/phone number. The form is supposed to pass the new key/values for the personal information if its changed. Instead of it doing that, the data is keeping the old changes and not updating the values for the new changes. userPersonalInfo is not updating in this case.
ModalVerification.vue
onVerifySuccess(existingData) {
// if no object exist, complete new form
if(!Object.keys(existingData).length) {
this.completeFormModal();
} else {
this.meetingDetails.is_authenticated_user = true;
this.updateMeetPlanInformation(this.getMeetingPlanFields(existingData);
// only return existing data if object not null // else update the existing data with new key/value pairs. Most likely wrong, because its not checking if any values in the object have been updated before passing.
const userPersonalInfo = (existingData) === null ? this.getUserPersonalInfo(existingData) : existingData;
vueAssign(this.meetingDetails, userPersonalInfo);
this.completeFormModal();
}
export function vueAssign(objVal, srcVal) {
Object.keys(srcVal).forEach((key) => {
Vue.set(objVal, key, srcVal[key]);
});
}
The problem is likely in vueAssign, but you haven't shown that method. I can still suggest solutions:
Object.assign
Use Object.assign to copy props from meetingDetails into userPersonalInfo, overwriting any common properties:
Object.assign(userPersonalInfo, this.meetingDetails)
const userPersonalInfo = {
a: 1,
b: 2,
}
const meetingDetails = {
a: 999,
c: 'hello',
}
Object.assign(userPersonalInfo, meetingDetails)
console.log({ userPersonalInfo })
Spread operator
Use the spread operator, which performs the same assignment:
let userPersonalInfo = /*...*/
userPersonalInfo = {
...userPersonalInfo,
...this.meetingDetails,
}
let userPersonalInfo = {
a: 1,
b: 2,
}
const meetingDetails = {
a: 999,
c: 'hello',
}
userPersonalInfo = {
...userPersonalInfo,
...meetingDetails,
}
console.log({ userPersonalInfo })

How to update/set the values of multiple Objects (with the same parameters) with the same values JavaScript/Typescript

What is the best way/how can I update two Objects with the same set of values?
The only method I know of, is by setting each property of each object concurrently. As per example below. Below I am using a method to populate the Objects, by passing the values as parameter in the method.
PLEASE NOTE: the individual parameter I pass in the method (populateIndividualDetails(individual: SelectedMemberIndividualData)) consists of many parameters I do not need, and is not in the format I desire. Hence the use of a method to assign the properties.
Additional Note: Both Objects I wish to populate have the same parameters, and is in the exact same format. The Objects have nested parameters.
Perhaps one could copy the 1st Object after it has been populated? 🤔
Example:
model = {
initials: '',
name: '',
address: {
streetName: '',
...
}
...
}
initialValues= {
initials: '',
name: '',
address: {
streetName: '',
...
}
...
}
populateIndividualDetails(individual: SelectedMemberIndividualData) {
this.model.initials = individual.initials;
this.initialValue.initials = individual.initials;
...
}
Rather than populating model and initialValues with empty key-value pairs, you could instead consider making an array of the properties which you want to be set in both the model and initialValues objects. Inside of populateIndividualDetails() you can then loop over this array with a for loop, and grab each property from the passed in individual object which you can then set on your model and initialValues objects.
const desiredProps = ["a", "b", "d"]; // contains "initials", etc...
const model = {};
const initialValues = {};
function populateIndividualDetails(individual) {
for(const prop of desiredProps) {
model[prop] = individual[prop];
initialValues[prop] = individual[prop];
}
}
populateIndividualDetails({a: 1, b: 2, c: 3, d: 4}); // ignore "c" value
console.log(model);
console.log(initialValues);
EDIT
If you need model and initialValues to be populated initially, then it might be better to create one and deep-clone the other (as you mentioned you can have nested object properties) and then use recursion to handle the nested objects:
const model = {a: '', b: {c: ''}, e: ''};
const initialValues = JSON.parse(JSON.stringify(model)); // deep-clone model, both are their own object references.
function populateIndividualDetails(o1, o2, individual) {
Object.keys(o1).forEach(key => {
if(Object(o1[key]) === o1[key])
populateIndividualDetails(o1[key], o2[key], individual[key])
else
o1[key] = o2[key] = individual[key];
});
}
populateIndividualDetails(model, initialValues, {a: 1, b: {c: 2, d: 3}, e: 4, f: 5}); // ignore "f" value
console.log(model);
console.log(initialValues);
You can just destructurate and pick the properties of individual the following way:
function populateIndividualDetails({initials}: SelectedMemberIndividualData) {
this.model.initials = initials;
this.initialValue.initials = initials;
}
but if you have several properties you might want this but this will replace the entire model and initial value objects and they will point to the same object…
function populateIndividualDetails(individual: SelectedMemberIndividualData) {
const {a, b, c} = individual;
const copy = {a, b ,c}
this.model= copy;
this.initialValue= copy;
}
What you are probably looking for is to just add some properties to the existing model and initialValue objects
function populateIndividualDetails(individual: SelectedMemberIndividualData) {
const {a, b, c} = individual;
const propertiesToAdd = {a, b ,c}
this.model= {...this.model, ...propertiesToAdd};
this.initialValue= {...this.initialValue, ...propertiesToAdd};
}
Note:
anObject = {...anObject, ...anotherObject};// can be replaced with
Object.assign(anObject,anotherObject);
Using lodash you could also do this:
function getDefaultProperties(anObject: SelectedMemberIndividualData){
return _.pick(anObject, ['a','b','c'])
}
function populateIndividualDetails(individual: SelectedMemberIndividualData){
const defaultProperties = getDefaultProperties(individual);
[this.model, this.initialValue].forEach((value) => {
_.assign(value, defaultProperties);
})
}

remove duplicates from array inside array of objects [duplicate]

This question already has answers here:
Get all unique values in a JavaScript array (remove duplicates)
(91 answers)
Closed 3 years ago.
This doesn't work
const uniqueArray = this.allusers.user_ids.filter((thing,index) => {
return index === this.allusers.user_ids.findIndex(obj => {
return JSON.stringify(obj) === JSON.stringify(thing);
});
});
this.allusers.user_ids = uniqueArray
This is the example of my json
0:{
code: "sdsdll"
id: 10
type: "New User"
unit: "Amount"
updated_at: "2019-08-20 09:01:24"
user_ids: (2) [2, 2, 3, 4, 3]
value: "12.00"
__proto__: Object
}
1: {code: "ssddl", id: 9, code: "sklsdsd",...........…}
2: {code: "sdds", id: 11, code: "dsfsdf232",...........…}
I want to remove the duplicates from user_ids array inside array of objects.
Try (something like this)*:
Immutable way:
this.allusers = this.allusers.map(userObj => {
return Object.assign({}, userObj, {
user_ids: Array.from(new Set(userObj.user_ids))
})
});
(*) iterating the object array and mapping each object to a new object with the duplicate user_ids removed by the new Set(...) and in the form of an Array by the Array.from(...)
Mutating original objects: (as #Kaddath mentioned in the comments this keeps other possible existing references to the user objects as it mutates them instead of replacing)
this.allusers.forEach(userObj => {
userObj.user_ids = Array.from(new Set(userObj.user_ids))
});
You can use a set to dedupe an array. All you then need to do is convert it back to an array: [...new Set(user_ids)]
const data = [{"code":"sdsdll","id":10,"type":"New User","unit":"Amount","updated_at":"2019-08-20 09:01:24","user_ids":[2,2,3,4,3],"value":"12.00"},{"code":"sdsdll2","id":101,"type":"New User 2","unit":"Amount","updated_at":"2019-08-20 09:01:24","user_ids":[4,11,2,2,3,4,4,3],"value":"12.00"}];
const out = data.map(({ user_ids, ...rest }) => {
const dedupedIds = [...new Set(user_ids)];
return { user_ids: dedupedIds, ...rest };
});
console.log(out);
Look like your allusers is an array so you can not call .user_ids on it. You can loop through it and process user_ids field of each element with Set.
let allusers = [{code: "sdsdll",id: 10,type: "New User",unit: "Amount",updated_at: "2019-08-20 09:01:24",user_ids: [2, 2, 3, 4, 3],value: "12.00"},{code: "sdsdll2",id: 101,type: "New User 2",unit: "Amount",updated_at: "2019-08-20 09:01:24",user_ids: [4, 2, 2, 3, 4, 4, 5],value: "12.00"}];
allusers.forEach((user) => user.user_ids = [...new Set(user.user_ids)]);
console.log(allusers);
If you want to delete the duplicate values, you can do as follows:
var user_ids = Array.from(new Set(user_ids));

Object Array to Hash Table

Would like to convert (using Ramda)
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}]
into
var b = {1:'one', 2:'two', 3:'three'}
I'm very new to functional programming and I am open to any ideas.
What I think we must do is, use a reduce function starting with {} and then we want to add on each iteration the id and name of the current element to the hashtable. This is what I came up with which appears very wrong. Am I close?
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'},{id: 4, name: 'four'} ]
var b = R.reduce(R.assoc(R.prop('id'), R.prop('name')), {}, a)
b
There are many approaches to this that will work. All the other correct answers helps show that. I'll list a few of my own below. But first,
Why your approach isn't working
You try to do this:
R.reduce(R.assoc(R.prop('id'), R.prop('name')), {}, a)
It's clear what you're trying to do here. The trouble is that R.prop('id') and R.prop('name') are functions. R.assoc does not accept functions; it wants a String (Number will actually serve) and an arbitrary value. So assoc will not work with these in this manner.
One attempt to clean this up is to recognize that functions can be thought of as containers of values. In some -- perhaps surprising, but quite useful -- way, a function is a container of its return value. R.lift is meant to turn functions that work on values into ones that work on containers of values. It works like this: R.multiply accepts numbers. If we lift multiply, the resulting function accepts containers of numbers. Like this:
R.lift(R.multiply)(Maybe.Just(5), Maybe.Just(3)) // Maybe.Just(15)
If we supply lift(multiply) with two functions, then we get back a function that returns the result of multiplying their return values:
const area = R.lift(R.multiply)(prop('width'), prop('height'))
area({width: 5, height: 3})
So perhaps we could update your technique with a lift:
R.reduce(R.lift(R.assoc)(R.prop('id'), R.prop('name'), identity), {}, a)
Unfortunately, this fails again. The trouble this time is that reduce takes a binary callback, supplying both the accumulator and the current value. But R.prop('id') and R.prop('name') don't recognize that. They look for the relevant properties on the accumulator, which simply are not there.
We might still be able to fix this, but at this point, we'd be losing a great deal of the simplicity of this solution. So let's look at other possibilities.
Some solutions
Simple Reduce
Each of these two versions uses a simple reduce call, as your solution tried to do:
const convertToHash = reduce((acc, {id, name}) => merge(acc, {[id]: name}), {})
and
const convertToHash = reduce((acc, {id, name}) => ({...acc, [id]: name}), {})
They are much the same. Both create disposable objects on each iteration. In the first one, you could replace R.merge with Object.assign without any real issues, as the resulting mutation is internal to the function. The second one seems slightly more elegant, but it rebuilds the entire accumulator on each iteration. As the engine optimization for ES6 proceeds, this will likely eventually not be a performance problem, but it might be right now, especially if this is in performance-critical code.
Using zipWith
Ramda's zip functions take two lists and combine them, position by position, into a single list. R.zipWith accepts a function used to combine the two elements into one. Here we use R.objOf, which turns a name and a value into a single-property object. (objOf('foo', 42) //=> {foo: 42}.):
const convertToHash = compmose(mergeAll, lift(zipWith(objOf))(pluck('id'), pluck('name')))
As above, we use lift to make this zipWith(objOf) work with functions. That results in something like [{"1": "one"}, {"2": "two"}, {"3": "three"}], which we then pass to R.mergeAll to create a single object.
Using props and fromPairs
This solutions uses R.props and R.fromPairs. fromPairs accepts a list of name-value pairs (as two-element arrays) and turns them into a single object. props pulls the named properties into a stand-alone array. Mapping this over the original list give us the input to fromPairs:
const convertToHash = compose(fromPairs, map(props(['id', 'name'])))
Although I'm fond of the zipWith solution, and would use it if its output was what I wanted, having to add the mergeAll, makes it harder to comprehend at a glance. And so this solution wins in my mind as the best Ramda choice.
You could achieve this by the following:
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}]
var b = R.reduce((dict, item) => ({ ...dict, [ item.id ] : item.name }), {}, a)
This approach uses es6 syntax to add keys (named via item.id) with value (item.name) to your resulting dictionary, during each iteration of the reduction.
You can create a pipeline (R.pipe) to convert your array of objects to a hash:
Get the values of each (R.map) objects' properties (R.props).
Convert the array of pairs to an object (R.fromPairs).
const a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}];
const convertToHash = (props) =>
R.pipe(
R.map(R.props(props)),
R.fromPairs
);
const result = convertToHash(['id', 'name'])(a);
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
Try using this code:
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}]
R.map(i=> {
var n = []
n[R.keys(i)[0]] = i.name
return n
},a)
when using es6 you can get a more flexible solution
let a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}];
let out = a.reduce((acc, {id, name}) => {
acc[id] = name;
return acc;
}, {});
console.log(out)
You can loop your initial array and assign to new object:
var a = [{id: 1, name: 'one'}, {id: 2, name: 'two'},{id: 3, name: 'three'}];
var new_a = {};
a.forEach(function(e) {
new_a[e.id] = e.name;
});
console.log(new_a);

Categories

Resources