Iterate array containing custom index - arrays

I am doing a small project in Angular8 and i have an array which contains data of users as object. This array is provided to me by the client which i can not change.
[
{
id:045#71,
name:'Ahmad',
isActive: false
},
{
id:047#71,
name:'John',
isActive: false
},
{
id:048#71,
name:'Doe',
isActive: false
}
]
In this array i have id's as custom indexes because of which i am not able to iterate this array. I am getting undefined in console when i try to iterate the array . Is there a way to iterate array with custom indexes. I even tried forEach loop but it is not working as well.
The method i used:
usersArray = [
{
id:045#71,
name:'Ahmad',
isActive: false
},
{
id:047#71,
name:'John',
isActive: false
},
{
id:048#71,
name:'Doe',
isActive: false
}
];
ngOnInit() {
this.usersArray.forEach((user)=>{
console.log(user.id + ' - ' + user.name);
})
}

What you have is what we typically call a collection. You can access an item from the collection if you know its index. In most cases, the index is an integer greater or equal to zero. i.e. collection[0] will give you the first item.
You can use collection.find(el => el.id ==='045#71') to find an element in the collection, if it doesn't exist will return undefined.
To find the index of an item you can use collection.findIndex(el => el.id ==='045#71'), in this case it will return 0
If you want to use customer indexes you would have to convert the collection into an object that uses your custom indexes as keys. There's surprisingly also a way to use custom keys in an array but it's not common practice in JavaScript.

Related

Instead of replacing the object it adds a new one in a nested array with React

Sup,
So I have this object:
data: {
OtherFields: {},
Skills: [
{
id: Math.random(),
name: 'Default Category',
skills: [],
},
],
{
So the Skills Array is very dynamic, I need to add categories, and each categories have their own array named skills, that will be filled with other objects, and the default category is there.
While the skills inside will have:
{
id: Math.random(),
skillName: 'Default Category',
}
What I want to do is add the skill to the specific category in a dynamic way with the id category as we don't know how much the user will add.
Here what I did until now:
const handleAdd = (id, content) => {
// id is the cateogry of that specific cateogry that im receiving from input
// content is the value of the input
// this is the object i need to push into the category
const newItem = {
id: Math.random(),
skillName: content,
};
// and then update it,
const newData = data.Skills.find((i) => i.id === id);
console.log(newData)
newData.skills.push(newItem);
setData({ ...data, Skills: [...data.Skills, newData] });
//this it works but adds another cateogry and doesnt not replace the current one with the new value that is added
};
This appends newData to the array:
Skills: [...data.Skills, newData]
But it doesn't filter that same record from the array when appending it. It basically means "the whole array as-is, plus this new element". Even if that element is conceptually a duplicate, the code doesn't know that. (Heck, even if it's by reference a duplicate, there's still nothing stopping an array from containing two references to the same object.)
It sounds like you want to filter that whole array to remove that element before re-appending it. For example:
Skills: [...data.Skills.filter(s => s.id !== newData.id), newData]
Since you're modifying the original object this should work, rename your variables to make it easier to read.
Also consider not changing the original object.
setData({ ...data, Skills: [...data.Skills] });

Angular MatTableDataSource filter by comparing nested array of values to array from multiselect

I have an Angular MatTableDataSource object with a number of top-level properties and a nested array property that contains a list of IDs that are relevant to that row.
Each entry in the MatTableDataSource array looks something like this:
IMSI: 505230000000006,
isActive: "Yes",
APNList: [1,2,3,4,5]
I also have a multi mat-select element from which I can select one or more of the IDs that relate to the entries in the APNList nested array.
I need to create a filter on the selectionChange event of the multiselect that will filter the MatTableDataSource by matching the array returned by the mat-select with the nested array in each entry.
So, for example, selecting 1 and 2 in the select will filter the MatTableDataSource to only entries that have 1 and/or 2 in its APNList array.
I feel like some combination of filterPredicate and ES6 some() will get the job done, but haven't quite gotten my head around how to achieve it.
Any help would be greatly appreciated!
You have two options, I'll start by making a random data source.
type entry = {
IMSI: number;
isActive: string;
APNList: number[];
};
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss'],
})
export class TestComponent implements OnInit {
dataSource: MatTableDataSource<entry> = new MatTableDataSource([
{ IMSI: 505230000000006, isActive: 'Yes', APNList: [1, 2, 3, 4, 5] },
{ IMSI: 505230000000007, isActive: 'Yes', APNList: [6, 7, 8, 9, 10] },
{ IMSI: 505230000000008, isActive: 'Yes', APNList: [1, 2, 3, 4, 5] },
]);
...
I'll hard code the check for 1 and 2 for simplicity, obviously your functions would have parameters to allow for different searches.
Option 1: Filter the data array directly using filter()
option1() {
const data: entry[] = this.dataSource.data;
const filteredData: entry[] = data.filter(
(entry) => entry.APNList.includes(1) || entry.APNList.includes(2)
);
console.log(filteredData);
}
filter() has a function as a parameter, the function needs to return true for the element to pass through the filter.
Option 2: Filter the data array indirectly using the MatTableDataSource class
option2() {
this.dataSource.filterPredicate = (entry: entry, filter: string) =>
entry.APNList.includes(1) || entry.APNList.includes(2);
//Data is only filtered after updating the filter string
this.dataSource.filter = 'Some filter string';
console.log('Option2: ', this.dataSource.filteredData);
}
We can provide a custom filter function as the filterPredicate property, the same as with filter(). However, this function has a second string parameter which corresponds to the filter property of the MatTableDataSource class. You are meant to use this filter string to filter your data.
The filtered data appears in the filteredData property of the class. Keep in mind, filteredData only updates when the filter property changes. Also, if the filter property is empty, filteredData will contain all data, regardless of the function. More info in the docs: https://material.angular.io/components/table/api#MatTableDataSource
Stackblitz: https://stackblitz.com/edit/angular-ivy-zamkgj?file=src/app/test/test.component.ts

MongoDB / Mongoose - When updating an array field with fewer elements problems

I have a collection that looks like:
name: {
type: String,
maxlength: 150,
required: true
},
description: {
type: String,
maxlength: 350
},
status: {
type: String,
default: 'active'
},
targets: [ {
type: Schema.Types.ObjectId,
ref: 'Thing',
index: true
} ]
});
The problem is with targets. Creating and adding to that array is no problem. However, if I reduce the number of elements in the array, it updates the targets, but does NOT reduce the size of the array, which causes numerous problems.
For example if targets = ["111111111111111111111111", "222222222222222222222222", "333333333333333333333333"]
and I do an update with targets = ["111111111111111111111111", "333333333333333333333333"],
the resulting array is ["111111111111111111111111", "333333333333333333333333", "333333333333333333333333"] since it doesn't reduce the size of the array.
I've looked at numerous things, and can't figure this out. The actual targets in my case can have several hundred elements. Also, doing an $addToSet doesn't seem to work, as it still won't remove the extra elements at the end. I really can't do a $slice, either - at least I haven't figured-out a way to do that. When I tried, I got an error saying that I couldn't update the same field twice.
How does one do this?
Here is the update code:
let filter = {
_id: aRecord._id
};
let update = aRecord;
MyCollection.findOneAndUpdate(filter, update, (err, insertStatus) => {
if (err) {
console.error(err);
return next(err);
}
if (1 === insertStatus.ok) {
res.status(200);
}
return res.json(insertStatus);
});
Thanks!
Seems stupid, but this works when reducing number of array elements of an array field:
{ $push:{ targets: { $each: sourceArray, $position: 0, $slice: sourceArray.length } } };
Basically, insert the array of elements in the front, then truncate the array to the length of the source array.
This assumes the source array has entire list of array elements. So, the front-end, user changes the number of checkboxes in a list - it sends the entire list of checkboxes, not a delta.

Remove duplicate values from an array of objects in reactjs

I have an array like so:
reportData = [
{status: "Resolved", type: Placement of bins},
{status: "Assigned", type: Placement of bins},
{status: "Active", type: Sewerage}
]
Now this is what I am trying to do. I want to make as many checkboxes as the count of reportData array but also remove the duplicated values, like in this case, I want "Placement of bins" only once (one checkbox). So overall according to the array there should be only 2 checboxes, but I'm getting three because obviously there are 3 objects in the array.
{
reportData.map((obj) => {
return (
<FormControlLabel
value="type"
control={<Checkbox color="primary" />}
label={obj.type}
labelPlacement="end"
/>
)
})
}
Is there some way to first sort out the duplicated array of objects and maybe then I can map it?
Here is what I would do,
const mapped = reportData.map((obj, index) => obj.type);
const filtered = mapped.filter((type, index) => mapped.indexOf(type) === index )
Here is what happened, map function made a new array called mapped that has the all types you had in reportData including duplicates.
filter filtered the duplicates as; array.indexOf(element) returns index of the first element that matches. Here, 'Placement of bins' is in 0 and 1 indexes. So the iterations be like true false true since in first iteration it is 0 === 0, in second: it's 0 === 1 and in third it's 2===2. So filtered array only gets the elements of index 0 and 2
And then using the filtered array to map to get checkboxes.
You could use lodash method _.uniqBy, it is available in the current version of lodash 4.17.2.
Example:
_.uniqBy([{status:"Resolved", type:'Placement of bins'},
{status:"Assigned", type:'Placement of bins'},
{status:"Active", type:'Sewerage'}], 'type');
// => [{status: "Resolved", type: "Placement of bins"}, {status: "Active", type: "Sewerage"}]
More info: https://lodash.com/docs/#uniqBy

Angular2 avoid adding duplicate JSON objects to array

I'm trying to avoid adding duplicate objects to an Angular2 array. For example let's say we have an array "Cars" and we put in a JSON object in that array that might be something like:
{
"brand": "Acura"
"engine": "2.4L"
+ others details
}
We can keep adding cars with to the array. My issue is now how to avoid adding the same car into the array again. If I were to add the same JSON as described above, it shouldn't be added. Would I have to iterate through all the existing objects in the array and compare each field of the JSON? This sounds like an expensive operation to me, or is there some more elegant/faster method?
Thanks!
If each car has something unique about it (like an ID), you could use an object instead of an array:
var map = { };
function add(car) {
if (map[car.id]) {
return;
}
map[car.id] = car;
}
Your ID could be something else unique, like a combination of the brand and year or something similar.
map[car.brand + car.year] = car
You need to find or create a unique property on the items in the list, then you could use something like lodash to query the array by a property to see if that item already exists in the list before you add it.
Example from Lodash site
var users = [
{ 'user': 'barney', 'active': false },
{ 'user': 'fred', 'active': false },
{ 'user': 'pebbles', 'active': true }
];
// The `_.matches` iteratee shorthand.
_.findIndex(users, { 'user': 'fred', 'active': false });

Resources