Searching from multiple keys inside object in javascript (reactJS) - javascript

i have the the following array of object which indicated all the possible combination of a product
[{id:25,attributes:[{id: 0, name: "Weight", option: "250gr"},{id: 0, name: "Roast", option: "Medium"},{id: 0, name: "Packaging", option: "Card"}
{id:26,attributes:[{id: 0, name: "Weight", option: "500gr"},{id: 0, name: "Roast", option: "Medium"},{id: 0, name: "Packaging", option: "Card"}
{id:27,attributes:[{id: 0, name: "Weight", option: "250gr"},{id: 0, name: "Roast", option: "Light"},{id: 0, name: "Packaging", option: "Card"}
{id:28,attributes:[{id: 0, name: "Weight", option: "250gr"},{id: 0, name: "Roast", option: "Light"},{id: 0, name: "Packaging", option: "Tin"}
]
On the webpage, options are set via select; the select also set a state component as follow:
{Weight:"",Roast:"",Pakaging:""}
with the values from the selected option, for example (assuming only weight and roast are selected):
{Weight:"250gr",Roast:"Light",Packaging:""}
everytime a option is selected i want to narrow down the combination of product (based on the selection) until i finally got only one option available (also the combination are used for dinamically repopulate select, adding or removing available options based on previous selections)
until now i was using something like (inside the function called by onChange on a select):
const result = variations.filter((p) =>
p.attributes.some((c) => c.option === event.target.value)
);
but this way it reduces based on the latest selection, so if i start selecting 250gr it gives me a set of 3 (25,27,28)
then if i select Medium, it give me a set of 2 (25 and 26) filtering only on medium and ignoring Weight
i'm looking to convert my filter, instead that on event.target.value, on the object but i cant find the right method, anyone can help?

You need to persist the selected filters like this:
const [filters, setFitlers] = React.useState({});
const onFilterChange = (e) => {
setFitlers(prevFilters => ({...prevFilters, [e.target.name]: e.target.name})
}
Now you can filter like this:
const result = variations.filter((p) =>
Object.entries(filters).every(([key, value]) => p.attributes[key] === value)
);
Obejct.entries will give you the key and value pairs of the filter object. And with every, you check if all the filters match.
Now you only have to give the input a name to fit your name:
<input name="weight" onChange={onFilterChange} .../>
To remove filters, maybe filter the filter object as well to remove empty strings:
Object.entries(filters).filter(([key,value) => value).every....

Related

React Cascading Dropdowns (Dynamic)

The scenario is as follows: I want to make a reusable cascading dropdown component, however every video/article that I've seen on the topic just uses hard coded dependent dropdowns such as: Country => State => City.
However, for my situation it will not always be the same dependencies. How can I support custom dependency for cascading dropdowns?
For a hardcoded example I would do something along the lines of having one useEffect for each of the options, and make the dependant options change when the parent changes.
I have an object example to iterate through of one page I am trying to accomplish this for:
[
{
key: 0,
name: "State",
parentQuestion: null,
inputType: "Dropdown",
},
{
key: 1,
name: "Sublocation",
parentQuestion: "State",
inputType: "Dropdown",
},
{
key: 2,
name: "Operation",
parentQuestion: "Sublocation",
inputType: "Dropdown",
},
{
key: 3,
name: "Installment Number",
parentQuestion: "Operation",
inputType: "Dropdown",
},
{
key: 4,
name: "Upload Directory",
parentQuestion: null,
inputType: "File",
},
{
key: 5,
name: "Download Directory",
parentQuestion: null,
inputType: "Directory",
},
]
Is it possible to accomplish this? Or must I hardcode the logic with different hooks for each page?
You can have the component built passing the array data as props, then iterating over each key on an element of the array, you have to build the selects using map. For the option values list you have to prepare another object such that
optionList={country:[],state:[],city:[]}
You have to maintain an object with selected options, and onchange of the selects you have to modify the object for selected options.
Edit
Also onchange of selects you have to modify the optionList object, reducing the options array, for example
if country is selected 'UK' then on the final object selected you add the value final_object={country:'UK'} and then in optionList you have to reduce the options for corresponding state and city

office-ui-fabric / fluent-ui Grouped DetailsList

Today I tried to use the Grouped DetailsList of the fluent-ui.
What I expected: I need to declare some groups, let's say red, blue, green and then just add to each item, I want to add to the List, a specific property, that maps the Item to the groups.
e.g.:
groups: [
{ key: 'red', name: 'Color: "red"'},
{ key: 'green', name: 'Color: "green"'},
{ key: 'blue', name: 'Color: "blue"' },
],
items: [...,
{ key: 'red',anyProp1: "abc", anyProp2: "dfg",...},
...,
]
What I found out I have to do: I need to sort the Array, which contains my items in that way, that all Items belonging to the Group red need to be in one block. e.g.: [red, red, red, blue, blue, green, green, green]. Now I needed to provide the information about startIndex and count to map my Array of items to the groups.
This is what a definition of a group could look like:
groups: [
{ key: 'groupred0', name: 'Color: "red"', startIndex: 0, count: 2, level: 0 },
{ key: 'groupgreen2', name: 'Color: "green"', startIndex: 2, count: 0, level: 0 },
{ key: 'groupblue2', name: 'Color: "blue"', startIndex: 2, count: 3, level: 0 },
],
I can't understand why they have done it this way (For me it's very inconvenient this way). So, while I'm more between a beginner and an intermediate in JS. I think the guys who implemented this are professionals. There must be a reason. Maybe it has something todo with performance? I could imagine that when it comes to very large lists, it performs better this way, but I'm not sure.
Does anybody knows some details about this and can explain?
Faced the same issue and got a clue here. Then bult my solution.
Following is the function to generate groups array from the given items list sorted by the grouping column:
function groupsGenerator(itemsList, fieldName) {
// Array of group objects
const groupObjArr =[]
// Get the group names from the items list
const groupNames = [...new Set(itemsList.map(item => item[fieldName]))]
// Iterate through each group name to build proper group object
groupNames.forEach(gn => {
// Count group items
const groupLength = itemsList.filter(item => item[fieldName] === gn).length
// Find the first group index
const groupIndex = itemsList.map(item => item[fieldName]).indexOf(gn)
// Generate a group object
groupObjArr.push({
key: gn, name: gn, level: 0, count: groupLength, startIndex: groupIndex
})
})
// The final groups array returned
return groupObjArr
}
Typed and with empty group name option variant of the Timus's answer
function generateGroups(items: any[], fieldName: string, emptyGroupName: string = '-'): IGroup[] {
const groups: IGroup[] = []
const groupNames = [...new Set<string>(items.map(item => item[fieldName]))]
groupNames.forEach(name => {
const groupItemsCount = items.filter(item => item[fieldName] === name).length
const groupStartIndex = items.map(item => item[fieldName]).indexOf(name)
groups.push({
key: name,
level: 0,
name: name ?? emptyGroupName,
count: groupItemsCount,
startIndex: groupStartIndex
})
})
return groups
}

React: Handling an onChange within mapping render

I have created a matrix/table using material ui and have also used a button toggle to select a value.
The table is a matrix where a member can see what their current membership is, and they can choose from other membership types based on age... seniors, juniors, infants.
https://codesandbox.io/s/64j65yrxpw
The demo above will give more clarity. The issue is that when I select an option of yes, no or maybe - all options across all people are selected rather than just that person in that row and across all membership types - (click an option and you will see the problem).
The below is what feeds the table:
const selected = [
{ personId: "0001657", fullName: "Joe Bloggs", seniorStatus: "Yes" },
{ personId: "0001666", fullName: "John Doe", seniorStatus: "No" }
];
and ideally will want something like this returned based on selections:
const newlySelected = [
{ personId: "0001657", fullName: "Joe Bloggs", seniorStatus: "Yes", juniors: "maybe" },
{ personId: "0001666", fullName: "John Doe", seniorStatus: "No", juniors: "no", infants: "yes" },
];
I appreciate that there are two issues here... the toggle not behaving individually and the return of desired json - any help would be awesome. thanks
You have a "too simple" state.
state = {
alignment: "left",
formats: ["bold"]
};
Your state.alignment used by all buttons. If you want to keep it in the form of single component you need to make it 2D array:
[
['<#1 btn value>', '<#2 btn value', ...],
[..., ..., ...]
]
Then when you call setState you need to change the relevant item in the 2D array based on the action location. You have to indexes, one of the row, one of the cell, you can use them.
UPDATE
New state - 2D array
value: [
["no", "no", "no"],
["no", "no", "no"]
]
New ToggleButtonGroup - using rowIdx and cellIdx to have the right position in the 2D array
<ToggleButtonGroup
className={classes.toggleButtonGroup}
value={value[rowIdx][cellIdx]}
exclusive
onChange={(event, val) => this.handleValue(event, val, rowIdx, cellIdx)}
>
New handleValue - using the rowIdx, cellIdx to change the value in the right position.
handleValue = (event, val, rowIdx, cellIdx) => {
const newValue = [...this.state.value]
newValue[rowIdx][cellIdx] = val
this.setState({
value: newValue,
})
}

Javascript Map a Collection

The Issue:
I'm attempting to build a simple search tool. It returns a search query by matching an id to another item with the same id. Without going into the complexities, the issue I'm having is that when my data was organized previously, the map function from javascript returned all the results perfectly. However, now that my data is structured a bit differently (a collection, I think?) ....the ids don't appear to be lining up which causes the wrong search results to show.
The function in question:
const options = this.props.itemIds.map((id) => (
<Option key={this.props.itemSearchList[id].id}>
{this.props.itemSearchList[id].name}
</Option>
));
When the data was structured like this it worked as expected:
Example of previous structure:
const items = [
{
id: 0,
name: "name 0",
tags: ['#sports', '#outdoor', '#clothing'],
},
{
id: 1,
name: "name 1",
tags: ['#sports', '#outdoor', '#clothing'],
},
{
id: 2,
name: "Name 2",
tags: ['#sports', '#outdoor', '#clothing'],
},
Now that the data is a ?collection...the map function doesn't work as anticipated and it returns improper results or none at all: I've been able to use the lodash Map function on this structure successfully in the past.
Here's a screenshot of the new data:
I believe a representative way to write out the example would be:
const newItems = [
0: {
id: 0,
name: "name here",
},
1: {
id: 1,
name: "name here",
},
]
Any recommendations for making this work or need more info? Perhaps I'm misunderstanding the issue entirely, but I believe it has to do with data structure and the map function from JS. I can see results returning, but the id's are not lining up appropriately anymore.
Here's a visual representation of the misalignment. The orange is the search input and it pulling the right result. The green is the misalignment of what it's actually showing because of the data structure and mapping (I assume).
The issue is you were using index and lining that up with id as a sort of pseudo-key which is...beyond fragile. What you should be doing is keying by id (meaing itemsshould be an object) and then having a seperate array that stores the order you want. So items would be an object keyed by id:
const items = {
1: {
id: 1,
name: "name 1",
tags: ['#sports', '#outdoor', '#clothing'],
},
2: {
id: 2,
name: "name 2",
tags: ['#sports', '#outdoor', '#clothing'],
},
9: {
id: 9,
name: "Name 9",
tags: ['#sports', '#outdoor', '#clothing'],
},
};
And then itemIds (which it appears you already have) is an array with the correct order:
const itemIds = [1,9,2];
And then they can be accessed in the right order by looping over that array, and getting the element by said key:
itemIds.map((id) => {
const item = items[id];
// do something with the item
}
Take a look at how Redux recommends normalizing state shape.
https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape
What you call "collections" and "maps" are actually arrays. Now one of the arrays has the objects exactly at the position in the array that matches the id:
items[5].id === 5
Now through sorting /mutating / whatever you change the order so that the element at a certain position doesnt have that as an id:
newItems[5].id // 7 :(
That means that you cannot access the item that easy anymore, you now either have to sort the array again to bring it into order, or you search for an object with the id:
newItems.find(item => item.id === 5) // { id: 5, ... }
Or you switch over to some unsorted collections like a real Map:
const itemsMap = new Map(newItems.map(item => ([item.id, item])));
So you can get a certain item with its id as:
itemsMap.get(5) // { id: 5, ... }
... but the whole thing doesnt have to do with Array.prototype.map at all.
Here was my simple solution:
const options = [];
this.props.itemList.forEach((item) => {
if (this.props.searchResults.includes(item.id)) {
options.push(<Option key={item.id}>{item.name}</Option>);
}
});
Let me know what you think (to the group that tried to help!)

finding a value in an object

I have an object:
{
id: 16,
defs: {
name: "Depot (Float)", field: "Depot"
}
}
And an array (which can have more than one object in it but for the purposes of this only has one):
[
{
Percentage Monthly Potential: 1,
Area Manager: "Ashar",
Business Unit: "Retail",
Cust no: 68345,
Depot Name: "Leicester",
Group Number: "",
Depot: 14,
Target: 46100
}
]
What I need to do is take the field value from the object and use it to find the key that it matches in the second object and retrieve the value of it, so in this case I should be getting 14.
Any help with this would be much appreciated.
Thanks for your time.
If you are using ES6, you can try this:
const field = lookupObject.defs.field;
const matches = array.map(arrayItem => {
return {
field,
value: arrayItem[field]
}
});
The matches array will contain the data you are interested in.

Categories

Resources