Remove duplicate values from an array of objects in reactjs - arrays

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

Related

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.

How to merge (equal and unequal length) arrays by Id on Created() in Vue2

I need a code advice, since not finding a solution...Potentially two part question:
On Created(), I have 2 arrays that I want to combine (one of arrays is an external Axios API)
'''
<template>
<div v-for="merged in mergedArray.slice(0, 5)">
Title: {{merged.title}}
<!-- this is initaiall from arrayAxios -->
Color: {{merged.color}}
<!-- this is initaiall from arrayVue -->
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
arrayAxios: null,
arrayVue: [
{id:1, name: "Paris", color: "blue"},
{id:1, name: "Milano", color: "green"},
{id:1, name: "London", color: "gray"}
],
mergedArray:null
}
},
created(){
axios.get('https://jsonplaceholder.typicode.com/albums/')
.then((response) => {
this.arrayAxios = response.data;
});
},
}
</script>
'''
1st part (option): The IDs match, equal length arrays.
On Created (or Mounted), the idea would be to push data from the Axios fetched array (arrayAxios in the example) into the already existing one (arrayVue in the example), and display the combined new result. The merging is to be done by matching the exact Ids, and thus just concatenating the existing properties of an element in the aaray, into a new array (mergedArray).
2nd part (less important for me, but interesting maybe for others): The Ids do not match fully, since unequal arrays
Lengths of arrays are different (does not matter which one), thus only portion of the Ids match, so it would have to be a loop to return to the initial element once the shorter arrays gets exhausted (fully concatenated) and assigned new properties.
For example, the Axios request (longer array) will get 100 response to only 3 arrayVue elements, so the first three Axios results would get assigned to the Ids 1, 2, 3 respectively of the arrayVue, and push into the mergedArray. However, the next 3 Axios will continue the loop and the results will again face the only three initial arrayVue elements.
For your first case, you can make use of a filter() to check whether the id in the two arrays matches or not.
this.arrayAxios.filter(arr => this.arrayVue.filter(a => a['id'] === arr['id']))
This will return those objects from arrayAxios which matches the id of arrayVue.

Iterate array containing custom index

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.

Updating React state as a 2d array?

Given this 2d array React state,
this.state =
board: [
[null, null, null, null],
[null, {id: 21, canSelect: false}, null, null],
[null, {id: 42, canSelect: false}, null, null],
[null, null, null, null]
]
}
I have 3 main questions regarding using setState to update this state:
1) How would I target a specific index within this 2d array state in React? Something like board[1][1]: {newObject}?
2) How would I update only the "canSelect" values for each?
3) If there were an unknown number of array indexes I would need to update (say between 2 and 8), how would I update only those indexes?
Any help is really appreciated :)
1) How would I target a specific index within this 2d array state in React?
To access let's say, the object with id = 21 do that:
console.log(this.state.board[1][1].id)
2) How would I update only the "canSelect" values for each?
To change a specific canSelect property do it in a immutable way:
onChange = e => this.setState(state =>({
...state,
board : state.board.map((arr, i) => arr.map((item,j) =>{
if(!item.hasOwnProperty('canSelect') return item
return {...item, canSelect: !state.board[i][j]}
}))
}))
If there were an unknown number of array indexes I would need to update (say between 2 and 8), how would I update only those indexes?
If you want non-consecutive arrays(sparse), just create an Object and then map it's keys instead of indexes:
const obj = {myKey : 'foo'}
console.log(obj.myKey) //foo
See more about sparse arrays in this question, but the gist here is do not use it, even if they do not take up more space than a "normal" array, what you really want is a hashing mecanism that map key names to values, good old JSON
Update
Based on your comments I've realized that I misunderstood the third problem, but I'm not excluding it cause it can be useful.
So let's assume you want to update the canSelect property on every id contained in a list:
const idsToUpdate = [1,22]
But you don't know if the given ids exist on your current collection, the solution would be iterate through every item, check if they aren't null, then check if the id is inside the idsToUpdate list and only then update the canSelect property:
this.setState(state =>({
...state,
board : state.board.map((arr,i) => arr.map((item, j) =>{
if(!item) return item
if(!idsToUpdate.includes(item.id)) return item
return {...item, canSelect: true}
}))
}))

Convert an iterable map object to array object in ReactJS

I'm trying to load some json file in a DropdownList from react-widgets. When I load the json file the data type looks like this:
Map {size: 1, _root: ArrayMapNode, __ownerID: undefined, __hash: undefined, __altered: false}
__altered
:
false
__hash
:
undefined
__ownerID
:
undefined
_root
:
ArrayMapNode
size
:
1
__proto__
:
KeyedIterable
While the Dropdown component (from react-widgets) needs an array! So It makes this error:
Failed propType: Invalid prop `data` of type `object` supplied to `DropdownList`,
expected `array`. Check the render method of `Uncontrolled(DropdownList)`.
I can't load the json file directly and I have to use ajax to load it (or technically I can but it's a huge file and each time the user click on the dropdown list it takes couple of seconds to load the data from file). How can I convert this to an array?
P.S. The json file looks like this:
{
"items":[
{
"id": "aaa",
"name": "english"
},
{
"id": "aab",
"name": "Swedish"
},
]
}
I think you're looking for something along the lines of:
var dropdownList = jsonObject.items.map((item, i) => {
return (
<option key={i} val={item.id}>{item.name}</option>
);
})
The Array.prototype.map function simply goes over a list and produces a new list of the same size but with changes based on the modifier/callback function.
Update based on comment:
Based on the docs for DropdownList, you have two options for passing data to the list: a flat array of item names, or a list of objects with specific keys for the item name and value.
Option 1: flat list of item names
var itemNames = jsonObject.items.map((item) => item.name );
Option 2 : list of objects
var items = jsonObject.items.map((item) => {
return {
textField: item.name,
valueField: item.id
}
});
The benefit to using the second option would be when someone selects one of the items, the event that is created with point to the val attribute of the HTML <option>. This is useful for you especially since your ids and names are not the same. An example:
Option 1 (from above):
<DropwdownList
data={itemNames}
onSelect: (event) => {
// Will give the `val` attribute set by DropdownList
// This might be the item name, or it might be something unique to DrowdownList
console.log(event.target.value);
}
/>
Option 2 (from above):
<DropwdownList
data={items}
onSelect: (event) => {
// Will give the `valueField` attribute you set, which for you is the item's `id` field
// This might be the item name, or it might be something unique to DrowdownList
console.log(event.target.value);
}
/>

Resources