I want to be able to do this:
for(let i = 0; i < this.props.user.cart.length; i++) {
return (
<div>
{"products.desserts." + this.props.user.cart[i] + ".name"}
</div>
);
}
And have JSX read it as a call to my json. Instead, it just spits out a string.
The string it gives is products.desserts.cakes.cake1.name, which is correct if it would actually access the name in my json file.
I've seen some stuff about dynamic variable names and I'm not completely sure if that's the right direction to go since I keep getting errors when attempting it.
(Very new to React by the way)
You can access an object key with strings instead of a dot . .
for(let i = 0; i < this.props.auth.cart.length; i++) {
return (
<div>
{products.desserts[this.props.user.cart[i].name]}
</div>
);
}
Edit
As a followup to your comment:
I want it to be accessing "products.desserts.cakes.cake1.name"
This cakes.cake1 is not a valid key for an object, hence you would need to split the . of the value in your array and use double brackets.
So in your case:
{
const splitedKeys = this.props.user.cart[i].split(".");
const key1 = splitedKeys[0];
const key2 = splitedKeys[1];
products.desserts[key1][key2].name
}
A simple example:
const products = {
desserts: {
cakes: {
cake1: {
name: 'cake 1'
},
cake2: {
name: 'cake 2'
}
}
}
}
const cart = ['cakes.cake1', 'cakes.cake2'];
cart.forEach((val) => {
const splitedKeys = val.split('.'); // split the values by the "."
const key1 = splitedKeys[0] // left side of the "."
const key2 = splitedKeys[1] // right side of the "."
console.log(products.desserts[key1][key2].name)
});
If you are trying to output a string products.desserts.someValue.name, then this will work:
for(let i = 0; i < this.props.auth.cart.length; i++) {
return (
<div>
products.desserts.{this.props.user.cart[i]}.name
</div>
);
}
What you have would work as well, but this is a little cleaner.
If you want to output the variable products.desserts.someValue.name, then you can do something like:
for(let i = 0; i < this.props.auth.cart.length; i++) {
return (
<div>
{products.desserts[this.props.user.cart[i]].name}
</div>
);
}
Basically, only treat the part that is actually dynamic as dynamic, and treat the rest like a normal variable.
Some gotchas:
A component must return a react element(basically a div , span etc in JSX) or null. Make sure you are returning one and only one react element and not multiple. If there are more, wrap them in one and return the wrapper.
If the code you have given is returned from a component, you may do as following:
const items= this.props.auth.cart.map((item, index) => {
let val = products.desserts[this.props.user.cart[index]];
return (
<div>
{val && val.name}
</div>
);
});
Then just return items wrapped in a div.
Remove the string quotes and wrap your JSX in {}:
for(let i = 0; i < this.props.auth.cart.length; i++) {
const item = products.desserts[this.props.user.cart[i]];
return (
<div>
{item ? item.name : 'N/A'}
</div>
);
}
Related
I have a controlled <input /> in my React application where the user would type.
I want to keep track on it and replace the occurrences of $.text and $.lang to random hashes/number.
from
let string = 'I may have multiple $.text and $.lang like $.textand$.lang';
I want all occurrences of $.text and $.lang to have a random number(or anything unique):
to
updatedString = 'I may have multiple 123 and 245 like 744and111';
What I have done so far
let temp = value.split('').reverse().join(''); // reverse it to find the latest occurrence
let reText = /txet.\$/i;
let reLang = /gnal.\$/i;
let updated;
if (value.includes('$.text')) {
updated = temp.replace(reText, `${hash}#`); // replace the latest occurrence
} else {
updated = temp.replace(reLang, `${hash}#`); // replace the latest occurrence
}
updatedValue = updated.split('').reverse().join(''); // re reverse it
The issue is it replaces the but onChange RESETS the input state and it only changes the last occurrence of it.
So im doing it on click. So lets say user type something and then clicking on Replace Template button will replace the $.text and $.lang by some random number. You can insert your specific hash or number if needed.
const replaceTemplateByRandom = (val, temp) => {
while(val.indexOf(temp) != -1){
const numb = Math.floor(Math.random()*1000);
val = val.replace(temp, numb);
}
return val;
}
inside this arrow function I'm running the while loop till we find the template to replace, and replace occurrence by random number.
const replaceTemplateByRandom = (val, temp) => {
while(val.indexOf(temp) != -1){
const numb = Math.floor(Math.random()*1000);
val = val.replace(temp, numb);
}
return val;
}
function App(props){
const [val, setVal] = React.useState('default text is $.text, and language is $.lang.');
const updateVal = (e) => {
const newVal = e.target.value;
setVal(newVal);
}
const replaceTemplate = ()=>{
let currentVal = val;
currentVal = replaceTemplateByRandom(currentVal, '$.text');
currentVal = replaceTemplateByRandom(currentVal, '$.lang');
setVal(currentVal);
}
return <div>
<input type="text" value={val} onChange={updateVal} style={{"width":"100%"}}/>
<button onClick={replaceTemplate}>Replace Template</button>
</div>;
}
ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
You can make use of .replace()'s acceptance of a function for custom and dynamic substitution:
function myHashFunction(match)
{
// Uncomment this line to see the various matches coming through
//console.log(match);
// I'm just returning a random number, you need to change this to your hash algorithm
return Math.floor(Math.random() * Math.floor(999999999999));
}
let str = `I may have multiple $.text and $.lang like $.textand$.lang`;
console.log(str.replace(/\$\.(?:text|lang)/g, myHashFunction));
function myFunction() {
var theString = "How are $.you doing $.you today?";
var splitString = theString.split("$.you");
var joinedString = "";
for(index = 0; index < splitString.length-1; index++){
joinedString += splitString[index] + Math.random();
}
document.getElementById("demo").innerHTML = joinedString;
}
Something simple like this could probably do the job.
So spliting the string into an array of strings and then joining it back with a specific string.
Not React specifically, but the idea is there.
I am currently receiving object segregatedData as props. This object contains several properties but the one we will focus on is segregatedData.type1 Basically this is just an array of strings like this ['water','fire','water',earth'] What I'd like to is to be able to print out each string and then beside it is how many instances this value has been repeated. So in the example case the expected output would be water-2, fire-1, earth-1 Of course we'd have to delete the duplicates.
Here's what I have so far:
import React from "react";
import { Typography } from "#material-ui/core";
function TypesText(props) {
const { segregatedData } = props;
return (
<>
<Typography>Type1:</Typography>
{segregatedData.length && segregatedData.map((data) => data.type1 + " ")}
</>
);
}
export default TypesText;
Fairly basic, it just prints every type1 property with a blank string as its seperator. Anything that can point me in the right direction is appreciated.
use segregatedData.type1.reduce to output array that finds occurrence in output array via mapping it and using string.search to find occurrence and then incrementing integer at the end.
Take a look at following code sample. test it
const arr = ['water','fire','water', 'earth'];
let val = arr.reduce((resultarr, item) => {
const val = resultarr.find(item1 => item1.search(item) !== -1)
const ind = resultarr.findIndex(item1 => item1.search(item) !== -1)
if (val == undefined) {
resultarr.push(item + "-" + 1)
}
else {
const i = val.search("-")
const num = parseInt(val.substring(i+1))
const nval = item + "-" + (num+1)
resultarr[ind] = nval;
}
return resultarr
}, [])
alert(val);
I would like to know how to filter array of values using javascript
How to seperate the arrays with 'provider-send' and 'provider-receive'
var filterradio = id.filter(function(e){
return e.id.split("-")[0] == "provider-send"
})
var id=["provider-send-credit-transfer", "provider-send-debit-fund","provider-receive-credit-transfer","provider-receive-debit-fund"]
Expected Output:
result_sn: ["provider-send-credit-transfer", "provider-send-debit-fund"]
result_rcn:["provider-receive-credit-transfer","provider-receive-debit-fund"]
If it's always going to be "provider-receive-..." and "provider-send..." then you can do the following to separate them
for (i = 0; i < id.length; i++) {
if (id[i].split("provider-send-").length > 1) {
result_sn.push(id[i]);
} else if (id[i].split("provider-receive-").length > 1) {
result_rcn.push(id[i])
}
}
Try This :
var id = [ "provider-send-credit-transfer", "provider-send-debit-fund","provider-receive-credit-transfer","provider-receive-debit-fund" ] ;
var result_sn = [] , result_rcn = [] ;
for( value of id ) {
var twoWords = value.split('-',2).join('-') ;
if ( twoWords === "provider-send" )
result_sn.push( value ) ;
else if ( twoWords === "provider-receive" )
result_rcn.push( value ) ;
}
console.log( result_sn ) ;
console.log( result_rcn ) ;
Pass 2 as second parameter to split() and then again join() by -. The second parameter to split() specifies the max number of elements in the result array.
var id=["provider-send-credit-transfer", "provider-send-debit-fund","provider-receive-credit-transfer","provider-receive-debit-fund"]
var filterradio = id.filter(function(id){
return id.split("-",2).join('-') === "provider-send"
})
console.log(filterradio)
Using .filter() will require you to write 1 filter for every pattern you want to match have assigned to a variable.
Using .reduce() is recommended and is more easily expandable to support more patterns.
It may look intimidating at first but you're essentially using accumulator as the temporary variable that gets stored and brought forward to each iteration. And each iteration of the array will give you the current iterated value with currentValue.
I've added something-else as an example of adding new pattern.
var id = [
"provider-send-credit-transfer",
"provider-send-debit-fund",
"provider-receive-credit-transfer",
"provider-receive-debit-fund",
"something-else-credit-transfer",
"something-else-debit-fund"
];
const {
'provider-send': result_sn,
'provider-receive': result_rcn,
'something-else': result_ste
} = id.reduce(function(accumulator, currentValue) {
let prefix = currentValue.match(/^\w+-\w+/)[0];
return {...accumulator, [prefix]: (accumulator[prefix] || []).concat(currentValue)}
}, {});
console.log(result_sn);
console.log(result_rcn);
console.log(result_ste);
I suggest using reduce instead of the filter as reduce is used to reduce the array size into a single returned element.
Here I am reducing the array into an object which has two keys result_sn and result_rcn.
var id = ["provider-send-credit-transfer", "provider-send-debit-fund", "provider-receive-credit-transfer", "provider-receive-debit-fund"]
const result = id.reduce((obj, str) => {
if (str.match(/^provider-send/g))
obj['result_sn'].push(str);
else
obj['result_rcn'].push(str);
return obj;
}, {
'result_sn': [],
'result_rcn': []
});
console.log(result)
Input: 'text __city__ text __city__ text __city__'
Output: 'text <i>city</i> text <i>city</i> text <i>city</i>'
The result of the feature is passed into dangerouslySetInnerHTML.
This component is
<div dangerouslySetInnerHTML={{__html: content.replace(/(?:\r\n|\r|\n)/g, '<br />')}}></div>
for this problem (link).
My function is
const transformText = (text) => {
const tmp = text.split('__');
var res = '';
var check = true;
for (var i = 0; i < tmp.length-1; i++){
check = !check;
if (check) {
res += '<i>'+tmp[i]+'</i>';
} else if (tmp[i] != undefined){
res += tmp[i];
}
}
return res;
};
but breaks the rest of the page into IE11 browser.
Step 1: transforming the input text.
Assuming the input is in the form "text __city__ text __city__ ... text
__city__" as per your example.
const input = "text1 __paris__ text2 __london__ text3 __berlin__";
// the following will turn the input into an array of the form
// [ [text, city], [text, city], ... [text, city] ]
const pairs = input.slice(0, -2).split('__ ').map(pair => pair.split(' __'));
Step 2: using the array in a React component.
Assuming the variable pairs holds the text/city pairs as defined above, the following would be the render method inside your component (if it is class-based).
render() {
return (
<ul>
{pairs.map((pair, index) => {
return <li key={index}>{pair[0]} <i>{pair[1]}</i></li>;
})}
</ul>
);
}
For some reason the following filter functionality I wrote is not appending to my results array properly. It seems to insert X occurrences of the last result object rather than the X number of result objects of the filter matches. Replacing "results.push(result)" in both places with "console.log(result)" returns the proper/different X result objects in my console window. Any idea why this strange behavior is occurring?
The goal is to have a results array of X number of result objects with each result object containing pertinent values from parent/sibling objects of each matching show name (such as day & time parents and network, title, & episode siblings).
// simple filter used to search show names
filter: function(el, string) {
console.clear();
string = $.trim(string);
var results = [];
var result = {};
if ( string.length ) {
for ( var i=0; i < el.length; i++ ) {
for ( var j=0; j < el[i]["time"].length; j++ ) {
if ( _.isArray(el[i]["time"][j].show) ) {
for ( var k=0; k < el[i]["time"][j].show.length; k++ ) {
if ( el[i]["time"][j].show[k]["#attributes"]["name"].search(new RegExp(string, "i")) > -1 ) {
result.day = el[i]["#attributes"]["attr"];
result["time"] = el[i]["time"][j]["#attributes"].attr;
result.show = el[i]["time"][j].show[k]["#attributes"]["name"];
result.sid = el[i]["time"][j].show[k].sid;
result.network = el[i]["time"][j].show[k].network;
result["title"] = el[i]["time"][j].show[k]["title"];
result.ep = el[i]["time"][j].show[k].ep;
result["link"] = el[i]["time"][j].show[k]["link"];
results.push(result);
}
}
}
else {
if ( el[i]["time"][j].show["#attributes"]["name"].search(new RegExp(string, "i")) > -1 ) {
result.day = el[i]["#attributes"]["attr"];
result["time"] = el[i]["time"][j]["#attributes"].attr;
result.show = el[i]["time"][j].show["#attributes"]["name"];
result.sid = el[i]["time"][j].show.sid;
result.network = el[i]["time"][j].show.network;
result["title"] = el[i]["time"][j].show["title"];
result.ep = el[i]["time"][j].show.ep;
result["link"] = el[i]["time"][j].show["link"];
results.push(result);
}
}
}
}
}
console.log(results);
}
For a live demo of the issue at hand, please visit my test page here. For convenience, I'm logging the large object through which the filter function is meant to iterate over to the console when the page fully loads to give an idea of the structure of that object so my filter function makes more sense to others (readers of this post).
Also here is how I'm calling the filter:
// filter show names for user input
$(".search").children("input").keyup(function() {
var str = $(this).val();
ctv.filter(ctv.xml.DAY, str);
});
Okay, seems like a somewhat silly oversight on my part. "Clearing the result var (ie. result = {};) just before beginning to set the attributes did the trick. Thanks for making me think around that area koala_dev. :)