Filtering and object in javascript - javascript

In my react app i'm coding a small photo gallery, with a GraphQL query i get all the images in a folder in this format:
{
"data": {
"allFile": {
"edges": [
{
"node": {
"childImageSharp": {
"fluid": {
"aspectRatio": 0.7518796992481203,
"originalName": "music_01.jpg"
}
}
}
},
{
"node": {
"childImageSharp": {
"fluid": {
"aspectRatio": 1.3333333333333333,
"originalName": "music_02.jpg"
}
}
}
},
{
"node": {
"childImageSharp": {
"fluid": {
"aspectRatio": 0.7518796992481203,
"originalName": "food_01.jpg"
}
}
}
}
]
}
},
"extensions": {}
}
it return about 50 entries, that (using links on the page) i need to filter out, something like a cateogry, where the category name is based on regex /category/ (or with indexOf) (music, foood and so) in the filename.
i was thinking to use a state to keep the original data separated from the filtered one, but it looks im not able to filter out the data to keep only the needed one.
my approach was something like
const Portofolio = ({data}) =>{
const [filtered, setFiltered]=useState();
function onFilterData(filter) {
//scroll through data and keep only the entries that match filter
//assign the kept data to filtered with setFiltered(keptdata)
}
return (
//render the gallery from filtered object
)
}
but im stuck on the filtered part and i cant get out of it!
any suggestion?

You can use useMemo to save the filtered list and avoid to use useEffect and setState and with this approach you save one render every time that your data changed.
You need something like this:
const filteredList = React.memo(() => {
return data.filter(({ childImageSharp: { fluid } }) => {
return fluid. originalName.indexOf(filter) > -1;
})
}, [data, filter])
data: is all your items list
filter: is the route that need to match with the item name

You use JS filter, check if your originalName exists and if it matches with your filter that comes from the fn argument
function onFilterData(filter = 'music') {
const edges = data?.allFile?.edges;
if (edges) {
const target = edges.filter(edge => {
const fileName = edge?.node?.childImageSharp?.fluid?.originalName;
if (fileName && fileName.includes(filter)) {
return true;
}
return false;
});
setFiltered(target);
}
}

Related

How to check nested JSON property and value exist in JavaScript?

I am getting below JSON as API response. Response contains multiple session node, each session node contains multiple event node. I would like to identify the first "SEARCH" event ("type") in any of the session ascending. If the "SEARCH" event is found, then, from the particular event, I need to get the "Interest" node value.
var guestJson = await response.json();
for(var property in guestJson)
{
if(property === "sessions")
{
var sessionObj = JSON.parse(JSON.stringify(guestJson[property]))
for(var i=0; i< sessionObj.length; i++)
{
var eachSessionObj = JSON.parse(JSON.stringify(sessionObj[i]))
for(var sessionProperty in eachSessionObj)
{
if(sessionProperty === "events")
{
console.log("Events Found")
//console.log(sessionProperty)
}
}
}
}
}
I am able to do with for loop like below. But i think it's not the effective way of doing that
Below is the JSON structure
{
"firstName":"fn",
"lastName":"ln",
"gender":"male",
"sessions":[
{
"currency":"USD",
"events":[
{
"type":"SEARCH",
"status":"PROCESSED",
"arbitraryData":{
"interest":"Health"
}
},
{
"type":"CHECK",
"status":"PROCESSED",
"arbitraryData":{
"interest":"Dental"
}
}
]
},
{
"currency":"USD",
"events":[
{
"type":"SEARCH",
"status":"PROCESSED",
"arbitraryData":{
"interest":"Health"
}
},
{
"type":"CHECK",
"status":"PROCESSED",
"arbitraryData":{
"interest":"Dental"
}
}
]
}
]
}
You could try a function like this:
function extractInterest(guestJson) {
// Get all sessions or else get an empty array
const sessions = guestJson.sessions || [];
// Filter all sessions with events
const interest = sessions.filter(session => session.events)
.flatMap(session => session.events) // Maps all the events to a single list
.filter(event => event.type === "SEARCH") // Filter only the events with type "SEARCH"
.map(event => event.arbitraryData.interest); // Extract the interest from each event
return interest; // return the list of interests
}
When applied to your example JSON, this returns an array of interests like this:
[ 'Health', 'Health' ]

Construct MongoDB query from GraphQL request

Let's say we query the server with this request, we only want to get the following user's Email, My current implementation requests the whole User object from the MongoDB, which I can imagine is extremely inefficient.
GQL
{
user(id:"34567345637456") {
email
}
}
How would you go about creating a MongoDB filter that would only return those Specified Fields? E.g,
JS object
{
"email": 1
}
My current server is running Node.js, Fastify and Mercurius
which I can imagine is extremely inefficient.
Doing this task is an advanced feature with many pitfalls. I would suggest starting building a simple extraction that read all the fields. This solution works and does not return any additional field to the client.
The pitfalls are:
nested queries
complex object composition
aliasing
multiple queries into one request
Here an example that does what you are looking for.
It manages aliasing and multiple queries.
const Fastify = require('fastify')
const mercurius = require('mercurius')
const app = Fastify({ logger: true })
const schema = `
type Query {
select: Foo
}
type Foo {
a: String
b: String
}
`
const resolvers = {
Query: {
select: async (parent, args, context, info) => {
const currentQueryName = info.path.key
// search the input query AST node
const selection = info.operation.selectionSet.selections.find(
(selection) => {
return (
selection.name.value === currentQueryName ||
selection.alias.value === currentQueryName
)
}
)
// grab the fields requested by the user
const project = selection.selectionSet.selections.map((selection) => {
return selection.name.value
})
// do the query using the projection
const result = {}
project.forEach((fieldName) => {
result[fieldName] = fieldName
})
return result
},
},
}
app.register(mercurius, {
schema,
resolvers,
graphiql: true,
})
app.listen(3000)
Call it using:
query {
one: select {
a
}
two: select {
a
aliasMe:b
}
}
Returns
{
"data": {
"one": {
"a": "a"
},
"two": {
"a": "a",
"aliasMe": "b"
}
}
}
Expanding from #Manuel Spigolon original answer, where he stated that one of the pitfalls of his implementation is that it doesn't work on nested queries and 'multiple queries into one request' which this implementation seeks to fix.
function formFilter(context:any) {
let filter:any = {};
let getValues = (selection:any, parentObj?:string[]) => {
//selection = labelSelection(selection);
selection.map((selection:any) => {
// Check if the parentObj is defined
if(parentObj)
// Merge the two objects
_.merge(filter, [...parentObj, null].reduceRight((obj, next) => {
if(next === null) return ({[selection.name?.value]: 1});
return ({[next]: obj});
}, {}));
// Check for a nested selection set
if(selection.selectionSet?.selections !== undefined){
// If the selection has a selection set, then we need to recurse
if(!parentObj) getValues(selection.selectionSet?.selections, [selection.name.value]);
// If the selection is nested
else getValues(selection.selectionSet?.selections, [...parentObj, selection.name.value]);
}
});
}
// Start the recursive function
getValues(context.operation.selectionSet.selections);
return filter;
}
Input
{
role(id: "61f1ccc79623d445bd2f677f") {
name
users {
user_name
_id
permissions {
roles
}
}
permissions
}
}
Output (JSON.stringify)
{
"role":{
"name":1,
"users":{
"user_name":1,
"_id":1,
"permissions":{
"roles":1
}
},
"permissions":1
}
}

Logic App/JavaScript - Remove Matching Values

I have two data sources. Data Source 1 is basically a list of Projects and Users assigned to those Projects. Data Source 2 is a list of Users that I want to remove from the Projects in Data Source 1.
Data Source 1:
[
{
"db1Project":[
{
"projectCode":"1",
"assignment":{
"db1User":[
{
"assignee":"wantzc"
},
{
"assignee":"michelles"
}
]
}
},
{
"projectCode":"2",
"assignment":{
"db1User":[
{
"assignee":"stallinga"
},
{
"assignee":"domanl"
},
{
"assignee":"brantleyd"
}
]
}
},
{
"projectCode":"3",
"assignment":{
"db1User":[
{
"assignee":"cinnamonk"
}
]
}
}
]
}
]
I want to match "assignee" from Data Source 1 with "sponsor" in Data Source 2.
Data Source 2:
[
{
"db2Users":[
{
"sponsor":"wantzc"
},
{
"sponsor":"patem"
},
{
"sponsor":"stallinga"
},
{
"sponsor":"oliviaa"
},
{
"sponsor":"brantleyd"
}
]
}
]
Then remove non-matching "assignee" and generate below output:
Desired Output:
[
{
"db1Project":[
{
"projectCode":"1",
"assignment":{
"db1User":[
{
"assignee":"wantzc"
}
]
}
},
{
"projectCode":"2",
"assignment":{
"db1User":[
{
"assignee":"stallinga"
},
{
"assignee":"brantleyd"
}
]
}
}
]
}
]
Can this be done using Logic App only? If not, how to do this using JavaScript?
This is a little bit of a verbose way to do it in Javascript, but the idea is relatively simple:
Create a map of the users you're looking for so you have an O(1) lookup instead of O(n) lookup each time
Filter the users list for each project to only contain users found in the db2Users list
Add the project to the new results set if any db1Users remain after step 2
This will take 1 pass through your original array, so it's an efficient, yet simple algorithm to do what you're trying to do.
let data = [{"projectCode":"1","assignment":{"db1User":[{"assignee":"wantzc"},{"assignee":"michelles"}]}},{"projectCode":"2","assignment":{"db1User":[{"assignee":"stallinga"},{"assignee":"domanl"},{"assignee":"brantleyd"}]}},{"projectCode":"3","assignment":{"db1User":[{"assignee":"cinnamonk"}]}}];
let db2Users = [{"sponsor":"wantzc"}, {"sponsor":"patem"}, {"sponsor":"stallinga"}, {"sponsor":"oliviaa"}, {"sponsor":"brantleyd"}];
let db2UsersMap = db2Users.reduce((res, curr) => {
res[curr.sponsor] = true;
return res;
}, {});
let filteredProjects = data.reduce((res, project) => {
let currProjUsers = project.assignment.db1User;
project.assignment.db1User = currProjUsers.filter((user) => db2UsersMap[user.assignee]);
if (project.assignment.db1User.length > 0) { res.push(project); }
return res;
}, []);
console.log(filteredProjects);

Vue.js swap array items

In my vue.js application I'm trying to swap 2 forum rows like this:
export default {
data() {
return {
forums: []
}
},
methods: {
increment(forum, index) {
ForumService.increment(forum)
.then(() => {
let b = this.forums[index];
this.forums[index] = this.forums[index++];
this.forums[index++] = b;
});
}
}
}
But nothing happens? What am I doing wrong here?
While #dfsq is correct about the use of index++ Vue doesn't recognize native mutations of arrays due to the inability to observe them. you have to use a mutation method to change them.
try this:
.then(() => {
let rows = [this.forums[index], this.forums[index + 1]];
this.forums.splice(index, 2, rows[1], rows[0] );
});
I haven't tested it and I'll edit when I can.

normalizr v3 and JSON api

I want to normalise the responses I receive from an API. A typical response could look something like this:
// Get all projects
{data:[
{
id: 1
...
team:{
data: {
id:15
...
}
}
},
{
id:2,
....
},
{
id:3,
...
}
]}
How do I write my schemas so that it removes the 'data' container?
Currently, my schema looks like:
export const project = new schema.Entity('projects', {
team: team, // team omitted
},
{
processStrategy: (value, parent, key) => parent.data
}
)
export const arrayOfProjects = new schema.Array(project)
And I am using it like:
const normalizedProjects = normalize(jsonResponse, arrayOfProjects)
normalizedProjects then looks like this:
{
entities:{
projects:{
undefined:{
0:{
team:{
data:{
id:15,
...
}
}
},
1:{...},
2:{...}.
...
50:{...},
}
}
},
result:[] // length is 0
}
I'm not sure why the list of projects is contained in 'undefined', either?
I also use json_api schema.
How about like this?
const projectsSchema = new schema.Entity('projects', {}, {
processStrategy: processStrategy
});
export const processStrategy = (value, parent, key) => {
const attr = value.attributes;
delete value.attributes;
return { ...value, ...attr };
};
export const fetchProjectsSchema = {
data: [projectsSchema]
}
Each of your entity schema that you want to have the data omitted (or anything else fundamentalyl changed) needs to include a processStrategy that you write to remove or change any data. (see more examples in the tests)

Categories

Resources