React - wrap multiple elements in parent node - javascript

I have the following array of words that compose some sentences:
let words = [
{
"start_time": "2.54",
"end_time": "3.28",
"alternatives": [
{
"confidence": "1.0",
"content": "Hello",
"__typename": "Alternative"
}
],
"type": "pronunciation",
"__typename": "TranscriptItems"
},
{
"start_time": null,
"end_time": null,
"alternatives": [
{
"confidence": "0.0",
"content": ".",
"__typename": "Alternative"
}
],
"type": "punctuation",
"__typename": "TranscriptItems"
},
{
"start_time": "3.29",
"end_time": "3.52",
"alternatives": [
{
"confidence": "1.0",
"content": "This",
"__typename": "Alternative"
}
],
"type": "pronunciation",
"__typename": "TranscriptItems"
}
]
Also i have this variable that contain an array of snippets selected from the words above:
const snippets = [{ start: 2.54, end: 4.00, id: 12, snippetTitle: "This is the title" }];
Right now I am showing the words by doing a simple .map and returning <span>s for every word.
What I am trying to accomplish is the following: if the span i am currently on is between a snippet from snippets variable, wrap it in another div/component so i can highlight it.
So instead of <span>Hello</span>.<span>This</span> i would be able to wrap both spans in a new wrapper component or div like <Highlight><span>Hello</span>.<span>This</span></Highlight>

You can do something like this using the reduce function:
export default myComponent = () => {
let cache = [];
return (
<>
{myArray.reduce((accumulator, currentValue) => {
if(!isInSnippets(currentValue)){
if(cache.length > 0){ // return the cached value for highlight
accumulator.push( (<Highlight>
{cache.map((elt, i) => <span key={i}>{elt}</span>)
</Highlight>));
cache = [];
}
accumulator.push(<span>{currentValue}</span>);
}
else cache.push(currentValue);
return accumulator;
}}
</>
)
}
The accumulator will contain an array of jsx elements. The regular elements will be contained in spans, while the Highlighted elements will be contained all together within a Highlight component.
We store the Highlighted elements in a cache variable. When we face a "regular" element, we check if there is something in the cache. If there is, we put them all in a highlight element, and add it to the cache. Then we add the "current" regular value.

Related

Filter data using a multiselect. When selecting 2 or 3 options result need to throw only registers where options together exist

thank you in advance for reading me. So I have been working in a filte. Right now my filter works, however doesn't do what I want. The current status is. When I select 2 options or more. I get all the values inside the data that contains either optionA oder optionB.
See my example data below:
{
"_uid": "1",
"body": [
{
"_uid": "2",
"name": "John",
"image": {
"id": 6807178,
"filename": "https://",
"copyright": "",
"fieldtype": "asset",
"is_external_url": false
},
"gewerk": "Project Owner",
"skill": ["vuejs", "react", "symfony"],
"component": "person",
},
{
"_uid": "3",
"name": "Jean",
"image": {
"id": 6807182,
"filename": "https://",
"copyright": "",
"fieldtype": "asset",
"is_external_url": false
},
"gewerk": "UI",
"skill": ["svelte"],
"component": "person",
},
{
"_uid": "4",
"name": "Martha",
"gewerk": "Frontend",
"skill": ["vuejs", "react"],
"component": "person",
},
{
"_uid": "5",
"name": "Tom",
"gewerk": "UI",
"skill": ["svelte", "angular", "vuejs"],
"component": "person",
}
],
}
With that being says when I filter using this example combi(screenshot). I get Martha, Tom and John as a result. When what I actually want is to have only Tom as a result. because only Tom have both criterias together inside his skills data.
This is my current computed function:
filterPersonSkill() {
return this.getComponentPerson.filter((e) =>
e.skill.map((skill) => this.multiValue.includes(skill)).includes(true)
);
}
At the beginning I used includes instead of map and that worked half. Because I was getting the result only if I selected in the same order(in the multiselect) as the array skills was appearing. Example below
filterPersonSkill() {
return this.getComponentPerson.filter((e) =>
e.skill.includes(...this.multiValue)
);
}
Thank in advance for the advice and reading me.
I think, it will be much simpler, if you add checkbox for the user to use "exact" filtering, i.e. results which include only selected tags.
With such a checkbox you can do something like this:
// your vue component
export default {
data() {
return {
exactMatch: true,
}
},
methods: {
filterPersonSkillExactMatch() {
const result = [];
for (const p of this.getComponentPerson) {
if (p.skill.length === this.multiValue.length
&& this.multiValue.every(val => p.skill.includes(val))) {
result.push(p)
}
}
return result
}
// somewhere in your code (either computed prop or method):
filteredPersons() {
if (exactMatch) {
return this.filterPersonSkillExactMatch()
}
return this.filterPerson()
}
}
}

Query Comparing Arrays of Objects

I'm wondering how I can compare arrays of (nested) objects in Mongoose.
Considering the data below, I would like to get results when the name properties match. Could anyone help me with this?
Organisation.find( {
$or: [
{ "category_list": { $in: cat_list } },
{ "place_topics.data": { $in: place_tops } }
]
}
)
Let's say that this is the data stored in my MongoDB:
"category_list": [
{
"id": "197750126917541",
"name": "Pool & Billiard Hall"
},
{
"id": "197871390225897",
"name": "Cafe"
},
{
"id": "218693881483234",
"name": "Pub"
}
],
"place_topics": {
"data": [
{
"name": "Pool & Billiard Hall",
"id": "197750126917541"
},
{
"name": "Pub",
"id": "218693881483234"
}
]
}
And let's say that these are the arrays I want to compare against (almost the same data):
let cat_list = [
{
"id": "197750126917541",
"name": "Pool & Billiard Hall"
},
{
"id": "197871390225897",
"name": "Cafe"
},
{
"id": "218693881483234",
"name": "Pub"
}
]
let place_tops = [
{
"name": "Pool & Billiard Hall",
"id": "197750126917541"
},
{
"name": "Pub",
"id": "218693881483234"
}
]
When there are "multiple conditions" required for each array element is when you actually use $elemMatch, and in fact "need to" otherwise you don't match the correct element.
So to apply multiple conditions, you would rather make an array of conditions for $or instead of shortcuts with $in:
Organizations.find({
"$or": [].concat(
cat_list.map( c => ({ "category_list": { "$elemMatch": c } }) ),
place_tops.map( p => ({ "place_topics": { "$elemMatch": p } }) )
)
})
However, if you take a step back and think logically about it, you actually named one of the properties "id". This would generally imply in all good practice that the value is in fact ""unique".
Therefore, all you really should need to do is simply extract those values and stick with the original query form:
Organizations.find({
"$or": [
{ "category_list.id": { "$in": cat_list.map(c => c.id) } },
{ "place_topics.id": { "$in": place_tops.map(p => p.id) } }
]
})
So simply mapping both the values and the property to "match" onto the "id" value instead. This is a simple "dot notation" form that generally suffices when you have one condition per array element to test/match.
That is generally the most logical approach given the data, and you should apply which one of these actually suits the data conditions you need. For "multiple" use $elemMatch. But if you don't need multiple because there is a singular match, then simply do the singular match

Dynamically add array elements to JSON Object

I'm creating a JSON object from an array and I want to dynamically push data to this JSON object based on the values from array. See my code for a better understanding of my problem...
for(i=0;i<duplicates.length; i++) {
var request = {
"name": duplicates[i].scope,
"id": 3,
"rules":[
{
"name": duplicates[i].scope + " " + "OP SDR Sync",
"tags": [
{
"tagId": 1,
"variables":[
{
"variable": duplicates[i].variable[j],
"matchType": "Regex",
"value": duplicates[i].scopeDef
}
],
"condition": false,
},
{
"tagId": 1,
"condition": false,
}
],
"ruleSetId": 3,
}
]
}
}
I take object properties from the duplicates array that can have the following elements:
[{scopeDef=.*, scope=Global, variable=[trackingcode, v1, v2]}, {scopeDef=^https?://([^/:\?]*\.)?delta.com/products, scope=Products Section, variable=[v3]}]
As you can see, an object contain variable element that can have multiple values. I need to push to the JSON object all those values dynamically (meaning that there could be more than 3 values in an array).
For example, after I push all the values from the duplicates array, my JSON object should look like this:
name=Products Section,
rules=
[
{
name=Products Section OP SDR Sync,
tags=[
{
variables=
[
{
matchType=Regex,
variable=v3,
value=^https?://([^/:\?]*\.)?delta.com/products
},
{
matchType=Regex,
variable=trackingcode,
value=.*
},
{
matchType=Regex,
variable=v1,
value=.*
},
{
matchType=Regex,
variable=v2,
value=.*
}
],
condition=false,
},
{
condition=false,
tagId=1
}
],
ruleSetId=3
}
]
}
I tried the following code but without success:
for(var j in duplicates[i].variable) {
var append = JSON.parse(request);
append['variables'].push({
"variable":duplicates[i].variable[j],
"matchType": "Regex",
"value": duplicates[i].scopeDef
})
}
Please let me know if I need to provide additional information, I just started working with JSON objects.
First of all, you dont need to parse request, you already create an object, parse only when you get JSON as string, like:
var json='{"a":"1", "b":"2"}';
var x = JSON.parse(json);
Next, you have any property of object wrapped in arrays. To correctly work with it you should write:
request.rules[0].tags[0].variables.push({
"variable":duplicates[i].variable[j],
"matchType": "Regex",
"value": duplicates[i].scopeDef
})
If you want to use your code snippet, you need some changes in request:
var request = {
"name": duplicates[i].scope,
"id": 3,
"variables":[
{
"variable": duplicates[i].variable[j],
"matchType": "Regex",
"value": duplicates[i].scopeDef
}
],
"rules":[
{
"name": duplicates[i].scope + " " + "OP SDR Sync",
"tags": [
{
"tagId": 1,
"condition": false,
},
{
"tagId": 1,
"condition": false,
}
],
"ruleSetId": 3,
}
]
}
}
To understand JSON remember basic rule: read JSON backward. It means:
property
object.property
arrayOfObfects['id'].object.property
mainObject.arrayOfObfects['id'].object.property
and so on. Good luck!

Generating components through iteration

Something weird is going on:
This is my initial state (a .js file)
import moment from 'moment';
let date = moment();
var previousDate = moment("2015-12-25");
export const projects = [
{
"id": 0,
"metadata": {
"fields":
[{
"id": 1,
"order": 1,
"name": "Name",
"value": "Collection 1"
},
{
"id": 2,
"order": 2,
"name": "Created On",
"value": date
},
{
"id": 3,
"order": 3,
"name": "Last Modified On",
"value": previousDate
},
{
"id": 4,
"order": 4,
"name": "Status",
"value": "Filed"
}],
"tags":
[{
"id": 1,
"order": 1,
"value": "tag1"
},
{
"id": 2,
"order": 2,
"value": "tag2"
},
{
"id": 3,
"order": 3,
"value": "tag3"
},
{
"id": 4,
"order": 4,
"value": "tag4"
}]
}
}
This is ProjectsList.js:
import React from 'react';
import Project from './Project';
import { projects } from 'initialState';
export default (props) => {
return(
<div className="projectsList">
{projects.map(project => (
<article key={project.id}><Project fields={project.metadata.fields} /></article>
))}
</div>
)
}
And this one's Project.js:
import React from 'react';
export default (props) => {
return(
<ul className="fields">
{props.fields.map(field => <li key={field.id}>{field.name}</li>) }
</ul>
)
}
I am trying to render a bunch of projects in a list, and every project contains a bunch of metadata key-value pairs that it shows.
So basically, the wiring does not matter, it all works fine.
Except for this:
If you look up at the initial state file (first one up there), there is an array of multiple objects in fields. Each object shows 4 key-value pairs
id
order
name
value
Now, in Project.js, the line where I go
{props.fields.map(field => <li key={field.id}>{field.name}</li>) }
looks like I can switch the {field.name} for {field.id}, to show the id in text. Or I can go {field.order}, to display the order.
But weirdly enough, if I want to show the actual value of the field, like so {field.value}, it throws.
invariant.js?4599:38
Uncaught Invariant Violation: Objects are not valid as a React child (found: Mon Jun 20 2016 21:40:33 GMT-0400). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of `StatelessComponent`.
I even went as far (sigh) as changing the string value in every fields to val, juste to make sure value wasn't some kind of a reserved word.
Still threw.
Anybody can help me understand what I have done wrong, here?
Thanks Guys.
You are assigning to variable values to the value property in your state file, which are most likely not strings, but objects:
export const projects = [{
"id": 0,
"metadata": {
"fields":
[
...
{
"id": 2,
"order": 2,
"name": "Created On",
"value": date // one
},
{
"id": 3,
"order": 3,
"name": "Last Modified On",
"value": previousDate // and another one
},
...
]
...
}
}
If typeof children returns "object" (and children is neither an array, nor a ReactElement), it throws:
https://github.com/facebook/react/blob/dc6fc8cc0726458a14f0544a30514af208d0098b/src/shared/utils/traverseAllChildren.js#L169
Here's a simplest example to demonstrate this:
const IllegalComponent = () => <span>{{}}</span>
You are supposed to supply a string (or number) so that React could inline that as the children in <li>. Children should be something that's renderable and implements ReactNode.
If the children is an object, React would not know how to render it. You should explicitly convert the value to String.
Try this to see if it works:
{props.fields.map(field => <li key={field.id}>{field.value.toString()}</li>) }

Javascript looping through elements and adding to table

I'm having trouble finding a solution that will help me loop through a bunch of elements and putting the chosen values into a table. I've been able to withdraw some values but the method isn't dynamic.
Here is an example:
var Table = {
"credit": {
"link": "site link",
"logoUrl": "logo url",
"message": "message"
},
"groups": [
{
"labels": [
{
"name": "Western Conference",
"type": "conference"
},
{
"name": "Central Division",
"type": "division"
}
],
"standings": [
{
"stats": [
{
"name": "gp",
"value": 20
},
{
"name": "w",
"value": 17
},
{
"name": "l",
"value": 0
},
{
"name": "gf",
"value": 64
},
{
"name": "ga",
"value": 37
},
{
"name": "gd",
"value": 27
},
{
"name": "pts",
"value": 37
}
],
"team": {
"id": 12345,
"link": "team link",
"name": "team name",
"shortName": "team"
}
},
This is the structure of the elements. So far I've used this:
document.getElementById("sGamesPlayed").innerHTML=Table.groups[0].standings[0].stats[0].value;
to withdraw values. However there are more teams, stats and divisions so I would need some kind of loop to go through the elements and put the into a dynamic table.
I would consider you to look at http://underscorejs.org/.
it provides a bunch of utility functions that could help you,
for example, _.each() helps you loop through JSON properties.
for the sample objects you've given (after completing the missing brackets at the end),
_.each(Table.groups[0].standings[0].stats, function(stats){
console.log(stats['name']+","+stats['value'])
})
gives me:
gp,20
w,17
l,0
gf,64
ga,37
gd,27
pts,37
how it works is that you provide the object you want as the first argument and the function that you give as the second argument will be called with each element of the first argument (Assuming it is a list).
I would also urge you to look at underscore templating that you can use to render your table where i put the console.log :
http://net.tutsplus.com/tutorials/javascript-ajax/getting-cozy-with-underscore-js/
http://scriptble.com/2011/01/28/underscore-js-templates/
I guess your question is about filtering the values of the array standings. In order to do that you can use the jQuery grep function (if you want to use jQuery).
For example you can write:
var arr = $.grep(Table.groups[0].standings[0].stats, function(d){return d.value>25})
Which will give
arr = [{"name": "gf","value": 64}, {"name": "ga", "value": 37},{"name": "gd", "value": 27},{"name": "pts", "value": 37}]
If this is not what you meant, can you please create a jsFiddle with a sample of what you want?
Depending on what you want to do with the results, you can go over the object using a scheme like:
var groups, standings, stats, value;
groups = Table.groups;
// Do stuff with groups
for (var i=0, iLen=groups.length; i<iLen; i++) {
standings = groups[i].standings;
// Do stuff with standings
for (var j=0, jLen=standings.length; j<jLen; j++) {
stats = standings[j];
// Do stuff with stats
for (var k=0, kLen=stats.length; k<kLen; k++) {
value = stats[k].value;
// Do stuff with value
}
}
}
Of course I have no idea what the data is for, what the overall structure is or how you want to present it. But if you have deeply nested data, all you can do is dig into it. You might be able to write a recursive function, but it might also become very difficult to maintain if the data structure is complex.

Categories

Resources