sorry for my English at the start.
I have a problem with filtering a list of products posted in JSON. The code is written in react native with the use of react redux.
The object of each product looks like this:
ProductTest {
"barcode": "barcode",
"brand": "brand",
"category": "category",
"description": "description",
"details": "details",
"filters": Object {
"cienkie": false,
"farbowane": false,
"krecone_i_puszczace": false,
"normalne": false,
"oslabione": false,
"przetluszczajace": false,
"suche_i_zniszczone": false,
"wszystkie": true,
},
"id": "0",
"image": "image",
"ingredients": "ingredients",
"name": "name",
},
And the "appliedFilters" list looks like this:
Object {
"cienkie": false,
"farbowane": false,
"krecone_i_puszczace": false,
"normalne": false,
"oslabione": false,
"przetluszczajace": false,
"suche_i_zniszczone": false,
"wszystkie": false,
}
I don't know how to make the "appliedFilters" list to be compared with the "filters" for each product, and to return the matching products from the list.
If you have any ideas I would be greatful.
You can use the array filter method :
let keys = Object.keys(appliedFilters);
let filteredList = productList.filter(product => {
let matching = true;
keys.forEach(key => {
if(product.hasOwnProperty(key)){
if(!(product.filters[key] === appliedFilters[key])) matching = false;
}
}
if(matching) return product;
})
If you are sure that the attributes order of the filter and the product objects will not change, you can simplify like this :
let filteredList = productList.filter(product => JSON.stringify(product.filters) === JSON.stringify(appliedFilters))
Related
I have an object like this:
const objBefore:
{
"id": "3pa99f64-5717-4562-b3fc-2c963f66afa1",
"number": "5000",
"enabled": true,
"classes": [
{
"id": "2fc87f64-5417-4562-b3fc-2c963f66afa4",
"name": "General"
},
{
"id": "7ffcada8-0215-4fb0-bea9-2266836d3b18",
"name": "Special"
},
{
"id": "6ee973f7-c77b-4738-b275-9a7299b9b82b",
"name": "Limited"
}
]
}
Using es6, I want to grab everything in the object except the name key of the inner classes array to pass it to an api.
So:
{
"id": "3pa99f64-5717-4562-b3fc-2c963f66afa1",
"number": "5000",
"enabled": true,
"classes": [
{"id": "2fc87f64-5417-4562-b3fc-2c963f66afa4"},
{"id": "7ffcada8-0215-4fb0-bea9-2266836d3b18"},
{"id": "6ee973f7-c77b-4738-b275-9a7299b9b82b"}
]
}
The closest I got was: let {id, number, enabled, classes: [{id}]} = objBefore;
But it only gets me one id in classes. I've tried spreading above using [...{id}] or [{...id}]. Same thing.
I find it challenging to get the right mental model for how to think about this when it's on multiple levels. In my mind, when I say [...{id}] I'm thinking, "I want the id property as an object in the outer classes array, but give me every id in the array!"
Clearly I'm not thinking about this correctly.
I've tried it using map to get that part but I'm still having trouble combining it back to the original to produce the desired result. for example:
let classIds = objBefore.classes.map(({id}) => {
return {
id
}
})
(Using the map syntax, how can I destructure in the function the other keys that are one level higher?)
To combine them I started trying anything and everything, :
let {id, number, enabled, classIds} = {objBefore, [...classIds]} // returns undefined for all
I'd prefer to do it in one statement. But if that's not possible, then what's a clean way to do it using map?.
You can't destructure and map at the same time in the way you're looking to do it. The main purpose of destructuring assignment is to extract data from an array/object and not for manipulating data. In your case, as you're after an object with the same keys/value as your original object, just with a different classes array, I would instead suggest creating a new object and spreading ... the original object into that. Then you can overwrite the classes array with a mapped version of that array:
const objBefore = { "id": "3pa99f64-5717-4562-b3fc-2c963f66afa1", "number": "5000", "enabled": true, "classes": [ { "id": "2fc87f64-5417-4562-b3fc-2c963f66afa4", "name": "General" }, { "id": "7ffcada8-0215-4fb0-bea9-2266836d3b18", "name": "Special" }, { "id": "6ee973f7-c77b-4738-b275-9a7299b9b82b", "name": "Limited" } ] };
const newObj = {
...objBefore,
classes: objBefore.classes.map(({id}) => ({id}))
};
console.log(newObj);
How about using simple util method with object destructuring, spread operator and map
const objBefore = {
id: "3pa99f64-5717-4562-b3fc-2c963f66afa1",
number: "5000",
enabled: true,
classes: [
{
id: "2fc87f64-5417-4562-b3fc-2c963f66afa4",
name: "General",
},
{
id: "7ffcada8-0215-4fb0-bea9-2266836d3b18",
name: "Special",
},
{
id: "6ee973f7-c77b-4738-b275-9a7299b9b82b",
name: "Limited",
},
],
};
const process = ({ classes, ...rest }) => ({
...rest,
classes: classes.map(({ id }) => ({ id })),
});
console.log(process(objBefore))
In one line, you could do this:
const objAfter = { ...objBefore, classes: objBefore.classes.map(item => ({ id: item.id })) };
Or, if you prefer:
const objAfter = {...objBefore, classes: objBefore.classes.map(({id}) => ({id}))};
There isn't any way in object destructing to copy an entire array of objects into a different array of objects by removing properties so you use .map() for that.
I am trying to implement StatSig(Refer: https://docs.statsig.com/) feature into my react(Nextjs) application. I have created new feature gate changeComponentUI and Rule has been added as Environment Tier -> Any of - staging, development. From client side I am trying retrieve the data as const statsigFeatureOn = useGate('changecomponentui').value, here I am always getting false value even if it is ON.
In app.js I have initialized like,
<StatsigProvider
sdkKey={"client-PsdasdASQW6556aIOPASASsqwewqeGSsad"}
user={{ userId, }}
options={{ environment: { tier: 'staging' } }}
waitForInitialization={true}
>
<Component {...args} />
</StatsigProvider>
In browser's Network tab I am getting -
Request Payload as,
{"user":{"userID":"","statsigEnvironment":{"tier":"staging"}},"statsigMetadata":{"sessionID":"433h67a3-416c-4914-82fd-e3c2b12f7n05","sdkType":"react-client","sdkVersion":"0.5.1","stableID":"8d122318-6d18-2322-889a-83c10e44e46"}}
Output is (Preview),
{
"gates": {
"+ZpxDXVQ/Rbhf02jl1Yv91VU+X+c0Gq/DZM+CmjPJgc=": false,
"uwglh6w2Nas8RufxC82qrsVAohod9gsGpvaT6/1l7ts=": false,
"sKSndsyjj+9MYUFlHPcdavbtA38g1+PjhofnyTDSxU8=": false,
"w9AtEJ/+vqrqb1kh8KPvhN2Rd32mkwfR+gxvlesY4ac=": true,
"gUZn3VlwEVdqBs7NcXGWHpBueNz0rlZGTufpLeB8Fug=": false
},
"feature_gates": {
"+ZpxDXVQ/Rbhf02jl1Yv91VU+X+c0Gq/DZM+CmjPJgc=": {
"name": "+ZpxDXVQ/Rbhf02jl1Yv91VU+X+c0Gq/DZM+CmjPJgc=",
"value": false,
"rule_id": "",
"secondary_exposures": []
},
"uwglh6w2Nas8RufxC82qrsVAohod9gsGpvaT6/1l7ts=": {
"name": "uwglh6w2Nas8RufxC82qrsVAohod9gsGpvaT6/1l7ts=",
"value": false,
"rule_id": "",
"secondary_exposures": []
},
"sKSndsyjj+9MYUFlHPcdavbtA38g1+PjhofnyTDSxU8=": {
"name": "sKSndsyjj+9MYUFlHPcdavbtA38g1+PjhofnyTDSxU8=",
"value": false,
"rule_id": "default",
"secondary_exposures": []
},
"w9AtEJ/+vqrqb1kh8KPvhN2Rd32mkwfR+gxvlesY4ac=": {
"name": "w9AtEJ/+vqrqb1kh8KPvhN2Rd32mkwfR+gxvlesY4ac=",
"value": true,
"rule_id": "6ZcQ0LOgAi2kSd5QgbtJzJ",
"secondary_exposures": []
},
"gUZn3VlwEVdqBs7NcXGWHpBueNz0rlZGTufpLeB8Fug=": {
"name": "gUZn3VlwEVdqBs7NcXGWHpBueNz0rlZGTufpLeB8Fug=",
"value": false,
"rule_id": "default",
"secondary_exposures": []
}
},
"configs": {},
"dynamic_configs": {},
"sdkParams": {},
"has_updates": true,
"time": 1631164754145
}
How can I get true value here? Also in output there is one object with true value but I am not getting it is for which feature gate's data.
Please help me to solve this issue.
Make sure you are using the exact ID from the console. In this case, you might need to do const statsigFeatureOn = useGate('changecomponentui').value if the feature name is "ChangeComponentUI" but the id is "changecomponentui". I believe all ID's are currently lowercase, but maybe camel case is a good reason not to do that (or to be case-insensitive)
(Edit) The following code snippet works for me in next.js after creating a gate by the same steps:
import React from "react";
import { StatsigProvider, useGate } from "statsig-react";
export default function App(): JSX.Element {
return (
<StatsigProvider
sdkKey={"client-(redacted)"}
user={{ userID: "" }} // Fixed from userId, but since userID didn't matter both worked in this case
options={{ environment: { tier: "staging" } }}
waitForInitialization={true}
>
<TestComponent />
</StatsigProvider>
);
}
function TestComponent() {
const statsigFeatureOn = useGate("changecomponentui").value;
return <div>{String(statsigFeatureOn)}</div>;
}
I want to take items from this array (the way I save things on the client)
[
{
"id": "-Mdawqllf_-BaW63gMMM",
"text": "Finish the backend[1]",
"status": true,
"time": 1625248047800
},
{
"id": "-Mdawqllf_-BaW63gGHf",
"text": "Finish the middle-end[2]",
"status": false,
"time": 1625248040000
},
{
"id": "-Mdawqllf_-BaW63gGHd",
"text": "Finish the front-end[3]",
"status": false,
"time": 1625248040000
}
]
And turn them into this format for how I save it server side
{ "todos": {
"-Mdawqllf_-BaW63gMMM": {
"text": "Finish the backend[1]",
"status": true,
"time": 1625248047800,
},
"-Mdawqllf_-BaW63gGHf": {
"text": "Finish the middle-end[2]",
"status": false,
"time": 1625248040000,
},
"-Mdawqllf_-BaW63gGHd": {
"text": "Finish the front-end[3]",
"status": false,
"time": 1625248040000,
}
},
}
Basically i turn items into an array on the client to help with sorting and making use of arrays. But before sending it back need to put into the right format
Use .map() to loop over the array of objects to exctract the id property, so you can use it as the key of the new object.
Use Object.fromEntries() to create the new object from the array returned by .map().
const data = [
{
"id": "-Mdawqllf_-BaW63gMMM",
"text": "Finish the backend[1]",
"status": true,
"time": 1625248047800
},
{
"id": "-Mdawqllf_-BaW63gGHf",
"text": "Finish the middle-end[2]",
"status": false,
"time": 1625248040000
},
{
"id": "-Mdawqllf_-BaW63gGHd",
"text": "Finish the front-end[3]",
"status": false,
"time": 1625248040000
}
];
const todos = {
Todos: Object.fromEntries(data.map(obj => [obj.id, obj]))
};
console.log(todos);
#Barmar's solutions is nice.
For the sake of learning or others googling. You can also reduce the array to an object.
const todos = data.reduce((obj, item) => {
obj[item.id] = item
return obj
}, {})
const items = {
todos: {
...data
}
};
Assume that data is the array of objects.
Use the spread operator to copy all the array objects from data array to the todos object at key todos.
One important thing to note that you can't assign more than one objects without array to a single object key. You definately have to use the array to maintain all the objects under the one key.
Avoid using the hardcode index. Always use the spread operator
I have a two history objects that are podcasts and articles, i want to display both in the same screen in descending order by which time they were clicked,
Here are the variables of original article and podcast from DB
var { articles, articlesInHistory, podcastsInHistory, podcasts } = this.props.stores.appStore;
Here is my article Object from history: console.log("dataItem", articlesInHistory)
dataItem Array [
Object {
"currentTime": 1585439646,
"id": "156701",
Symbol(mobx administration): ObservableObjectAdministration {
"defaultEnhancer": [Function deepEnhancer],
"keysAtom": Atom {
"diffValue": 0,
"isBeingObserved": true,
"isPendingUnobservation": false,
"lastAccessedBy": 26,
"lowestObserverState": 2,
"name": "appStore#1.articlesInHistory[..].keys",
"observers": Set {},
},
"name": "appStore#1.articlesInHistory[..]",
"pendingKeys": Map {
Symbol(Symbol.toStringTag) => false,
"hasOwnProperty" => false,
"toJSON" => false,
},
"proxy": [Circular],
"target": Object {
"currentTime": 1585439646,
"id": "156701",
Symbol(mobx administration): [Circular],
},
"values": Map {
"id" => "156701",
"currentTime" => 1585439646,
},
},
},
,]
And podcast from history: console.log("dataItem", podcastsInHistory)
dataItem Array [
Object {
"currentTime": 1585439636,
"id": "4",
Symbol(mobx administration): ObservableObjectAdministration {
"defaultEnhancer": [Function deepEnhancer],
"keysAtom": Atom {
"diffValue": 0,
"isBeingObserved": true,
"isPendingUnobservation": false,
"lastAccessedBy": 26,
"lowestObserverState": 2,
"name": "appStore#1.podcastsInHistory[..].keys",
"observers": Set {},
},
"name": "appStore#1.podcastsInHistory[..]",
"pendingKeys": Map {
Symbol(Symbol.toStringTag) => false,
"hasOwnProperty" => false,
"toJSON" => false,
},
"proxy": [Circular],
"target": Object {
"currentTime": 1585439636,
"id": "4",
Symbol(mobx administration): [Circular],
},
"values": Map {
"id" => "4",
"currentTime" => 1585439636,
},
},
},
]
now i want to order the two components using currentTime in condition
for example if this podcast was first then i should return
<PodcastList navigate={navigate} podcast={podcast} key={index} />)
Or if the article is first then show
<SmallArticle key={index} article={article} />
i need them mixed not like articles on top and podcast bottom, i been searching arrays sort but couldn't solve it.
I want a condition based on currentTime and using an id to identify or match objects thanks.
As you don´t know from the data if its an article or a podcast (there is no attribute in your objects that tells you that), you can´t put them in the same list-array.
The only way to know if you should render an Article or a Podcast component is based on what list you are reading.
Keep two indexes, articleIndex = 0 and podcastIndex = 0 (you can keep that in your state), and read the actual article and podcast for respective list, and compare the current time. Then you will know what component to render, and advance the corresponding list index.
In pseudo code:
while articleIndex < articlesList.length && podcastIndex < podcastList.length do:
if articlesList[articleIndex].currentTime < podcastList[pocastIndex].currentTime do:
render <SmallArticle article={articlesList[articleIndex]} key ={articleIndex}/>//render SmallArticle
articleIndex += 1; //advance index
else do:
render <PodcastList podcast={podcastList[postcastIndex]} key={podcastIndex} />
podcastIndex +=1;
when the while statement finishes, is because one of the list has been fully traversed. You need to traverse the rest of the other and render the respect component.
If you show me some of your components code can help you with code in more detail, but I don´t know the context.
I'm looking at this template to build a web application: https://js.devexpress.com/Demos/WidgetsGallery/Demo/PivotGrid/FieldChooser/AngularJS/Light/
In the example there are static data. I have to retrieve them from the server. So, I wrote this:
$scope.testData = [];
$scope.pivotGridDataSource = new DevExpress.data.PivotGridDataSource({
fields: [{
caption: "Nome",
dataField: "fullName",
area: "row"
}, {
caption: "Country",
dataField: "country",
area: "column"
}, {
caption: "Count",
dataField: "countOne",
dataType: "number",
summaryType: "sum",
area: "data"
}],
store: $scope.testData
});
$scope.pivotGridOptions = {
allowSortingBySummary: true,
allowSorting: true,
allowFiltering: true,
showBorders: true,
dataSource: $scope.pivotGridDataSource,
fieldChooser: {
enabled: false
}
},
$scope.fieldChooserOptions = {
dataSource: $scope.pivotGridDataSource,
texts: {
allFields: "All",
columnFields: "Columns",
dataFields: "Data",
rowFields: "Rows",
filterFields: "Filter"
},
width: 400,
height: 400,
bindingOptions: {
layout: "layout"
}
};
// Now I call the server to retrieve data
$scope.getTestData = () => {
$scope.testData.length = 0;
result.forEach(e => {
$scope.testData.push(e);
);
$scope.pivotGridDataSource.reload();
}
$scope.getTestData();
The problem is that when the data are loaded, in the Fields below it shows just the fields written at the beginning (so the name, the count and the country). But I saw in the demo that it should be display ALL parameters of the object.
For example, if the object is so structured:
{ "name": "Test1", "country": "Germany", "creationDate": "xxx", "surname": "yyy" }
So, I expect that in the fields there should be ALL parameters, so name, country, creationDate, surname. So, I did this at the beginning:
I changed $scope.testData = [] into:
$scope.testData = [{ "name": "", "country": "", "creationDate": "", "surname": "" }]
so the component will preparare all fields. And this works. But what if the server gives me back an Object that has another parameters? How can I display them?
I tried so after the calling and before the reload():
let fields = $scope.pivotGridDataSource.fields();
let newField = {
llowExpandAll: false,
allowFiltering: true,
allowSorting: true,
allowSortingBySummary: true,
caption: "This is a new field",
dataField: "newField",
dataType: "string",
displayFolder: "",
index: fields.length
}
$scope.pivotGridDataSource.fields().push(newField);
$scope.pivotGridDataSource.reload();
But it doesn't work yet. Worse, it does not even initialize the Pivot.
The fieldChooser uses the store fields, in this case $scope.testData fields, in your code I see your store is first declared (as null or with some format as you described) and then you have a function to fill it.
I don't know how your code looks and why you create your store that way, but that is basically your problem (the flow).
In the sample code the flow is:
Store with data (static in this case)
PivotGridDataSource
FieldChooser
In your code the flow is:
Store (empty)
PivotGridDataSource
FieldChooser
Store (fill) --> at this point your FieldChooser has been initialized with the fields of the empty version of your store so not much to do (in Jquery you could re-create your object, you dan do it using Jquery within AngularJs see a simple code sample here and below)
$('#chartContainer').dxChart('instance').dispose();
$('#chartContainer').dxPieChart({
...
});
To avoid all of this you can just use the DevExpress.data.CustomStore and your flow will be basically identical to the demo.