I have the following data in an array.
tempAttachments:Array[2]
0:Object
_id:"12345-678910"
bytes:412051
file:File
size:411532
title:"someFile.csv"
headers: Array[3]
0: optionOne
1: undefined
2: optionTwo
3: undefined
4: undefined
5: optionThree
type:"file"
fileType:"typeOne"
1:Object
_id:"9999-2222"
bytes:12345
file:File
size:23456
title:"anotherFile.csv"
headers: Array[3]
0: optionOne
type:"file"
fileType:"typeTwo"
There are two elements I am interested in, and that is the _id and headers array. I am trying to end up with something like this
Array
(
[0] => Array
(
[id] => 12345-678910
[optionOne] => 0
[optionTwo] => 2
[optionThree] => 5
)
[1] => Array
(
[id] => 9999-2222
[optionOne] => 0
)
)
So essentially, the id and the index of the three options as these relate to their column in a file. The problem is, a file may have a maximum of three options (using the names above) however, they may only have one or two.
So I have started like this
const payload = {}
this.tempAttachments.forEach(function (attachment, index) {
payload[index] = {
id: attachment._id
}
})
What I am unsure about is how to map the indexes of the options, with the key set as their name, if they exist. What would be the best way to achieve this?
Thanks
This is an environment/setting for a classic combined approach of map (here: create a new type from each attachment type) and reduce (here: create/collect new option data only for valid header/option items) ...
const tempAttachments = [{
_id: "12345-678910",
bytes: 412051,
file: "File",
size: 411532,
title: "someFile.csv",
headers: [
"optionOne",
undefined,
"optionTwo",
undefined,
undefined,
"optionThree"
],
type:"file",
fileType:"typeOne"
}, {
_id: "9999-2222",
bytes: 12345,
file: "File",
size: 23456,
title: "anotherFile.csv",
headers: [
"optionOne"
],
type: "file",
fileType: "typeTwo"
}];
function collectValidOptionData(collector, optionItem, idx) {
// if (optionItem != null) {
// collector[String(optionItem)] = idx
// }
if (typeof optionItem === 'string') {
collector[optionItem] = idx;
}
return collector;
}
function createOptionTypeFromAttachment(attachmentType) {
return attachmentType.headers.reduce(collectValidOptionData, {
id: attachmentType._id
});
}
console.log(
'tempAttachments.map(createOptionTypeFromAttachment) : ',
tempAttachments.map(createOptionTypeFromAttachment)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
A clean way to do this, is using the Array.map method, it creates a new array from another.
(More Info: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
The code below will convert the tempAttachments to the new format, where is based in this structure:
[
{
id: AttachmentID
optionName: optionIndex
optionName: optionIndex
...
}
]
It will only add options that have a value, ignoring undefined options.
Solution:
const payload = tempAttachments.map(attachment => {
const newAttachment = {
id: attachment._id,
}
attachment.headers.forEach((option, index) => {
if (option) {
newAttachment[option] = index;
}
})
return newAttachment;
})
Something like this:
const tempAttachments = [{
_id: "12345-678910",
bytes: 412051,
file: 'File',
size: 411532,
title: "someFile.csv",
headers: [
'optionOne',
undefined,
'optionTwo',
undefined,
undefined,
'optionThree'
],
type: "file",
fileType: "typeOne"
}, {
_id: "9999-2222",
bytes: 12345,
file: 'File',
size: 23456,
title: "anotherFile.csv",
headers: [
'optionOne',
'optionTwo',
'optionThree'
],
type: "file",
fileType: "typeTwo",
}];
const output = tempAttachments.reduce((akku, item) => {
let akkuItem = {
id: item._id,
};
item.headers.forEach((value, index) => {
if(typeof value !== 'undefined') {
akkuItem[value] = index;
}
});
akku.push(akkuItem);
return akku;
}, []);
console.log(output);
Related
I have the following nested array of objects:
const data = [
{
product: {
id: "U2NlbmFyaW9Qcm9swkdWN0OjEsz",
currentValue: 34300,
},
task: {
id: "R2VuZXJpY1Byb2R1Y3Q6MTA",
name: "My Annuity",
},
instrumentDetails: [
{
instrument: {
id: "U2NlbmFyaW9JbnN0cnVtZW50OjEz",
supplier: {
id: "U3VwcGxpZXJJbnN0cnVtZW50OjUzNjQ",
supplierDetails: {
name: "Local - Class A",
},
},
currentValue: 44323,
},
assets: {
current: 1.2999270432626702,
fixed: 0.5144729302004819,
financial: 0.0723506386331588,
cash: 0.00006003594786398524,
alternative: 0.05214078143244779,
property: 0.548494862567579,
local: 0.10089348539739094,
global: 0,
},
},
],
},
{
product: {
id: "U2NlbmFyaW9Qcm9swkfefewdWN0OjEsz",
currentValue: 3435300,
},
task: {
id: "R2VuZXJpYfewfew1Byb2R1Y3Q6MTA",
name: "Living",
},
instrumentDetails: [
{
instrument: {
id: "U2NlbmFyadewwW9JbnN0cnVtZW50OjEz",
supplier: {
id: "U3VwcGxpZdwdwXJJbnN0cnVtZW50OjUzNjQ",
supplierDetails: {
name: "Local - Class B",
},
},
currentValue: 434323,
},
assets: {
current: 1.294353242,
fixed: 0.514434242004819,
financial: 0.07434286331588,
cash: 0.0000434398524,
alternative: 0.05242348143244779,
property: 0.543242567579,
local: 0.100432439739094,
global: 0,
},
},
],
},
];
The above data presents an array of products which consist of instruments that are described in instrumentDetails array. I am trying to find an instrument by supplier id and update its assets by multiplying all of the asset values by a given number.
Here is my function:
export const updateObject = (
productsArr: any,
supplierInstrumentId: string
) => {
return productsArr.map(
(product: any) => {
product.instrumentDetails.map(
(instrumentDetail: any) => {
if (
instrumentDetail.instrument.supplier.id ===
supplierInstrumentId
) {
instrumentDetail.assets.current = instrumentDetail.assets.current + 5;
instrumentDetail.assets.fixed= instrumentDetail.assets.fixed+ 5;
instrumentDetail.assets.financial= instrumentDetail.assets.financial+ 5;
instrumentDetail.assets.cash= instrumentDetail.assets.cash+ 5;
}
}
);
}
);
};
This function is giving an error :
Uncaught TypeError: Cannot assign to read only property 'current' of
object '#'
How can I deeply update the above data? Please help.
You need to return a new instrumentDetail-type object from the map function. Don't try to update the existing object.
(instrumentDetail: any) => {
const assets = instrumentDetail.instrument.supplier.id === supplierInstrumentId
? Object.fromEntries(
Object.entries(instrumentDetail.assets).map(([k, v]) => [k, v + 5])
)
:
instrumentDetail.assets;
return {
...instrumentDetail,
assets
};
}
Your product map is not returning which is why you're likely getting an undefined. I wasn't getting the typescript error which you mentioned above. This should leave the array in the state at which you intended.
const updateObject = (
productsArr: any,
supplierInstrumentId: string
) => {
return productsArr.map(
(product: any) => {
product.instrumentDetails.map(
(instrumentDetail: any) => {
if (
instrumentDetail.instrument.supplier.id ===
supplierInstrumentId
) {
instrumentDetail.assets.current += 5;
instrumentDetail.assets.fixed= instrumentDetail.assets.fixed+ 5;
instrumentDetail.assets.financial= instrumentDetail.assets.financial+ 5;
instrumentDetail.assets.cash= instrumentDetail.assets.cash+ 5;
return instrumentDetail;
}
}
);
return product;
}
);
};
I have this array in my next.js app
arr1
[
{
identifier: "60a17722225f2918c445fd19",
name: "Ben Awad",
_id: "60c94480b8d43c28d0a6eb73
},
{
identifier: "60a455d11fa62a1510b408f8",
name: "dev ed"
_id: "60bf62cede309f1a30fe88ab"
}
]
And i have this another big array
arr2
[
{
name: "Ben Awad",
_id: "60a17722225f2918c445fd19
},
{
name: "dev ed",
_id: "60a455d11fa62a1510b408f8"
},
{
name: "Katlyn",
_id: "60a52500ce96f30c14fdaff9"
},
{
name: "Mike",
_id: "60c95deeb8d43c28d0a6eb74"
},
{
name: "Kassandra",
_id: "60c960ddb8d43c28d0a6eb7a"
}
]
I want a new array who should have all users except for those who have similar ids with arr1
So this is the logic i did (Notice that arr1 and arr2 will change constantly)
Me = arr1
AllUsers = arr2
const LookFriends =
Me &&
AllUsers.filter(({ _id }) => {
return Me.friends.indexOf(_id) === -1;
});
console.log(LookFriends);
The output should be Katlyn, Mike and Kassandra, but the console.log says...
[
{
name: "Ben Awad",
_id: "60a17722225f2918c445fd19
},
{
name: "dev ed",
_id: "60a455d11fa62a1510b408f8"
},
{
name: "Katlyn",
_id: "60a52500ce96f30c14fdaff9"
},
{
name: "Mike",
_id: "60c95deeb8d43c28d0a6eb74"
},
{
name: "Kassandra",
_id: "60c960ddb8d43c28d0a6eb7a"
}
]
I'm really having a hard time trying to filter an array based on another array, what can i do ?
You need to use 'findIndex' in this case, then compare the _id field itself:
const LookFriends =
Me &&
AllUsers.filter(({ _id }) => {
return Me.friends.findIndex(friend => friend._id === _id) === -1;
});
If you use "indexOf", it will compare the entire object to just that _id value.
You can extract the identifiers from the first array and filter the second array if the element doesn't match any id in our object or our Set
// with an object
const ids = {}
arr1.forEach(user => {
ids[user.identifier] = true
})
const filtered = arr2.filter(user => !ids[user._id])
// or with a Set
const ids = new Set(arr1.map(user => user.identifier))
const filtered = arr2.filter(user => !ids.has(user._id))
Please have a try with this code.
Me &&
AllUsers.filter(({ _id }) => {
let bExist = false
Me.friends.map( (friend) => {
if ( friend._id === _id )
bExist = true
})
return bExist
});
Let me know if it works or not.
I have an array of objects, I need to delete a complete object based on the id
Input :
filters: [
{
key: "status",
label: "En attente",
value: "waiting",
id: 0
},
{
key: "dateDue[min]",
label: "15/12/2019",
value: "15/12/2019",
id: 1
},
{
key: "dateDue[max]",
label: "02/02/2020",
value: "02/02/2020",
id: 2
},
{
key: "bien",
values: [
{
label: "Studio Bordeaux",
value: 36,
id: 3
},
{
label: "Studio 2",
value: 34,
id: 184
}
]
},
{
key: "type",
values: [
{
type: "receipts",
label: "Loyer",
value: "loyer",
id: 4
},
{
type: "receipts",
label: "APL",
value: "apl",
id: 5
},
{
type: "spending",
label: "taxes",
value: "taxes",
id: 6
}
]
}
]
So I created a removeItem method with the id that must be deleted in parameters
removeItem method :
removeItem = (e, id) => {
const { filters } = this.state;
const remove = _.reject(filters, el => {
if (!_.isEmpty(el.values)) {
return el.values.find(o => o.id === id);
}
if (_.isEmpty(el.values)) {
return el.id === id;
}
});
this.setState({
filters: remove
});
};
I use lodash to make my job easier and more specifically _.reject
My issue is the following :
I manage to correctly delete the classic objects for example
{
key: "status",
label: "En attente",
value: "waiting",
id: 0
}
but my method however does not work for objects of the following form
{
key: "bien",
values: [
{
label: "Studio Bordeaux",
value: 36,
id: 3
},
{
label: "Studio 2",
value: 34,
id: 184
}
]
},
currently the whole object is deleted and not only the object in the values array according to its id
Here is my codesandbox!
thank you in advance for your help
EDIT
I found a solution with lodash (compact), I share my solution here :
removeIdFromCollection = id => {
const { filters } = this.state;
const newFilters = [];
_.map(filters, filter => {
if (filter.values) {
const valuesTmp = _.compact(
_.map(filter.values, value => {
if (value.id !== id) return value;
})
);
if (!_.isEmpty(valuesTmp)) {
return newFilters.push({
key: filter.key,
values: valuesTmp
});
}
}
if (filter.id && filter.id !== id) return newFilters.push(filter);
});
return newFilters;
};
removeItem = id => e =>
this.setState({
filters: this.removeIdFromCollection(id)
});
The values false, null, 0, "", undefined, and NaN are removed with lodash compact (_.compact(array))
Here is my updated codesandbox
You will need to filter the filters array and each values separately. Below is a recursive function which will remove items with the given id from the filters array and from the values property.
PS. This example is not using Lodash as I think it is not needed in this case.
removeIdFromCollection = (collection, id) => {
return collection.filter(datum => {
if (Array.isArray(datum.values)) {
datum.values = this.removeIdFromCollection(datum.values, id);
}
return datum.id !== id;
});
}
removeItem = (e, id) => {
const { filters } = this.state;
this.setState({
filters: this.removeIdFromCollection(filters, id),
});
};
The problem would be the structure of the object. You'll need to refactor for that inconvenient array out of nowhere for uniformity:
// Example
filters: [
...
{
key: "type",
values: [
{
type: "receipts",
label: "Loyer",
value: "loyer",
id: 4
},
...
]
...
}
// could be
filters: [
...
{
key: "type-receipts",
label: "Loyer",
value: "loyer",
id: 4
}
...
]
Repeat the pattern on all of it so you could just use the native array filter like this:
const newFilters = filters.filter(v => v.id !== id);
this.setState({
filters: newFilters,
});
I found a solution with lodash, I share it with you here :
removeIdFromCollection = id => {
const { filters } = this.state;
const newFilters = [];
_.map(filters, filter => {
if (filter.values) {
const valuesTmp = _.compact(
_.map(filter.values, value => {
if (value.id !== id) return value;
})
);
if (!_.isEmpty(valuesTmp)) {
return newFilters.push({
key: filter.key,
values: valuesTmp
});
}
}
if (filter.id && filter.id !== id) return newFilters.push(filter);
});
return newFilters;
};
removeItem = id => e =>
this.setState({
filters: this.removeIdFromCollection(id)
});
Here is my updated codesandbox
THE PROBLEM:
I have an array of Objects. And a currentObject, that I currently am viewing. I want to get the value from a Property that comes from the Array of Objects, if 2 other properties match.
Here is the Array, simplified:
ARRAY = [{
id: 1,
metadata: {author: "Company1"}
},
{
id: 2,
metadata: {author: "Company2"}
}
Here is the Object, simplified:
OBJECT = {
name: "Something
templateId: 2
}
So, basically, I want to return, the metdata.author information, if the ARRAY.id, matches the OBJECT.templateId..
Here is the code I wrote..
const getAuthorInfo = (connTemplates: ARRAY[], currentConn: ITEM_OBJECT) => {
connTemplates.find((connTemplate: ARRAY_ITEM_OBJECT) => connTemplate.id === currentConn.templateId);
};
console.log('Author Info:', connection); // This though returns the OBJECT, not the ARRAY_ITEM
Any ideas, on how to make this work? I tried to filter as well, with the same condition, but that returned undefined, when I called it in my ReactComponent.
is this what you need?
const arr = [{
id: 1,
metadata: { author: "Company1" }
},
{
id: 2,
metadata: { author: "Company2" }
}]
const obj = {
name: "Something",
templateId: 2
}
function getAuthorInfo(arr, obj) {
const arrItem = arr.find(item => item.id === obj.templateId)
return arrItem.metadata.author
}
console.log(getAuthorInfo(arr, obj))
You are on the right path:
const result = arr.find(f => f.id == obj.templateId).metadata.author;
const arr = [{
id: 1,
metadata: {author: "Company1"}
},
{
id: 2,
metadata: {author: "Company2"}
}]
const obj = {
name: "Something",
templateId: 2
}
const result = arr.find(f => f.id == obj.templateId);
console.log(result);
Question:
How can I removal all emailAddress that are empty, and if there are no emailAddresses for an approval, remove that approval too.
My current solution will remove approvals when emailAddress completely empty. But not when two emailAddresses are present and one is empty (see script output vs. expected output)
var request = {
approvals: [
{
type: 'media',
emailAddresses: [
{emailAddress: 'frank#gmail.com'},
]
},
{
type: 'other',
emailAddresses: [
{emailAddress: ''},
]
},
{
type: 'scope',
emailAddresses: [
{emailAddress: 'kelly#yahoo.com'},
{emailAddress: ''},
]
}
]
}
const filterOutEmptyEmails = (approval) => {
if(approval.emailAddresses.filter(x => !!x.emailAddress).length){
return true;
}
}
let output = request.approvals.filter(filterOutEmptyEmails);
console.log(JSON.stringify(output));
// EXPECTED OUTPUT:
// approval: [
// {
// type: 'media',
// emailAddresses: [
// {emailAddress: 'frank#gmail.com'},
// ]
// },
// {
// type: 'scope',
// emailAddresses: [
// {emailAddress: 'kelly#yahoo.com'},
// ]
// }
// ]
// }]
Live Code
You are not replacing approval.emailAddresses in your code - you should use:
approval.emailAddresses = approval.emailAddresses.filter(x => !!x.emailAddress);
See demo below:
var request={approvals:[{type:'media',emailAddresses:[{emailAddress:'frank#gmail.com'},]},{type:'other',emailAddresses:[{emailAddress:''},]},{type:'scope',emailAddresses:[{emailAddress:'kelly#yahoo.com'},{emailAddress:''},]}]};
var filterOutEmptyEmails = (approval) => {
approval.emailAddresses = approval.emailAddresses.filter(x => !!x.emailAddress);
if(approval.emailAddresses.length){
return true;
}
}
var output = request.approvals.filter(filterOutEmptyEmails);
console.log(JSON.stringify(output));
EDIT:
Another proposal without mutating the input array - using Array.prototype.reduce to create a new array:
var request={approvals:[{type:'media',emailAddresses:[{emailAddress:'frank#gmail.com'},]},{type:'other',emailAddresses:[{emailAddress:''},]},{type:'scope',emailAddresses:[{emailAddress:'kelly#yahoo.com'},{emailAddress:''},]}]};
var output = request.approvals.reduce(function(p,c){
// creates a shallow copy
var elem = Object.assign({},c);
// replaces the reference to request.approvals by the new array created by the filter
elem.emailAddresses = elem.emailAddresses.filter(x => !!x.emailAddress);
if(elem.emailAddresses.length != 0)
p.push(elem);
return p;
},[]);
// console.log(request.approvals);
console.log(output);
.as-console-wrapper{top:0;max-height:100%!important;}
Possible "non mutation" solution could be like this
var request = {approvals: [{type: 'media',emailAddresses: [{emailAddress: 'frank#gmail.com'},]},{type: 'other',emailAddresses: [{emailAddress: ''},]},{type: 'scope', emailAddresses: [{emailAddress: 'kelly#yahoo.com'},{emailAddress: ''},]}]}
const filterOutEmptyEmails = (approval) => {
if(approval.emailAddresses.filter(x => !!x.emailAddress).length){
return true;
}
}
const output = request.approvals.map(approval => {
const filteredAproval = approval;
filteredAproval.emailAddresses = approval.emailAddresses.filter(x => !!x.emailAddress);
return filteredAproval
}).filter(filterOutEmptyEmails);
console.log(JSON.stringify(output));
console.log(JSON.stringify(request));
Without mutation (with lots of ES6/7 sugar):
const filteredApprovals = request.approvals.reduce((acc, approval) => {
const filteredEmailAddresses = approval.emailAddresses.filter(item => item.emailAddress);
return (filteredEmailAddresses.length > 0) ? [...acc, { ...approval, emailAddresses: filteredEmailAddresses }] : acc;
}, []);
Fiddle: https://jsfiddle.net/free_soul/hndjbce3/