Creating Array out of comma seperated list in React JS - javascript

I have an input for users to come and paste one id or multiple with each seperated by a comma then run a function on the individual Id's.
<Input type={'text'} placeholder="Id(s) (seperated by comma if more than 1)" value={ids} onChange={(event) => setIds(event.target.value)} />
Then I have the React useState constants for using the individial ids
const [ids, setIds] = useState(["ID#1", "ID#2"]).split("[\\s,]+")
const [id, setId] = useState(addresses[0]);
Here is where I set the function for picking each individual ID and running the function
const fetchNft = async (e) => {
for(let i = 0; i< ids.length; i++){
const _id = id[i]
const idInfo = await find(_id);
console.log(idInfo)
}
}

This doesn't make any sense:
useState(["ID#1", "ID#2"]).split("[\\s,]+")
useState returns an array containing the state value and the setter function for the state value. There's nothing to "split", and in this case neither of those values are strings.
You probably meant to put that split logic here:
setIds(event.target.value.split("[\\s,]+")
Since the input value is the string of comma-separated values that you want to split into an array.

This can't possibly work
const [ids, setIds] = useState(["ID#1", "ID#2"]).split("[\\s,]+")
You need to first call useState and then split on the first returned value. You can't split the return value of useState given that it is an array.

Related

How to Add Default Elements to an Array when Requirements Not Met?

I have a code that converts user input to an array then does a fetch request. The rest of the code only works when the array has a quantity (as a string) and a unit (as a string). So for example, it works when I type in "1 whole apple" or "1 oz chicken thigh" but it breaks when it's just "apple". How can I set up a checkpoint to add "1" and "whole" to the beginning of the array when quantity and unit are missing?
const [input, setInput] = useState("")
const foodName = []
const searchArray = []
// This part below separates user input by space then forms an array
const inputArray = input.split(/(\s+)/).filter(e => String(e).trim())
// This part below forms a new array with only the quantity and the unit, or nested arrays if the user inputs more than one item, which obviously breaks if there is no quantity.
const array = inputArray.reduce(
(arrays, value) => (
isFinite(value)
? arrays.push([value])
: arrays[arrays.length - 1].push(value),
arrays
),
[]
)
// This part below combines food name to a single element if it has more than one word i.e. apple pie.
for (var i = 0; i < array.length; i++) {
const foodName = array[i].splice(2).join(' ')
foodArray.push(foodName)
const wholeArray = array[i].concat(foodArray[i])
searchArray.push(wholeArray)
}
Making the fetch request etc.
Thanks in advance!
So I ended up adding an error message since I could not find a solution to this.
if (!isFinite(inputArray[0])) {
setErrorMessage('Be sure to specify quantity and unit!')
return
}
const array = inputArray.reduce(
(arrays, value) => (
isFinite(value)
? arrays.push([value])
: arrays[arrays.length - 1].push(value),
arrays
),
[]
)

Dynamically populate string literally templates

Whenever I select a person from the list it grabs the id of a name and stores it an array with map.
I then have a string literal which gets populated with the ID.
const id = value.map(person => person.value)
console.log('from',id)
current output:
[u29219]
withe the results looking like this:
const results = await verifiedGet(`get_user/?$u29219?admin_form=True`, user.user)
and then if I add another person the array would look like this
[u29219, u302932]
results:
const results = await verifiedGet(`get_user/hello?$u29219,u302932?admin_form=True`, user.user)
When a user is added to the array I want to be able to iterate through the results with the ID only populating once if a user is selected twice
const results = await verifiedGet(`get_user/?$u29219?admin_form=True`, user.user)
const results = await verifiedGet(`get_user/?$u302932?admin_form=True`, user.user)
is this possible to do so?
I created a sandbox for a better understanding
https://codesandbox.io/s/modest-star-fegy7
I would take your unique id array and use Array.Join() to combine them with commas.
// Map the ids, and filter unique
const uniqueIds = value
.map((person) => person.value)
.filter((value, index, self) => self.indexOf(value) === index);
// Join the Ids and separate them with commas
const commaSeparatedIds = uniqueIds.join[','];
// Don't forget to make them safe for a URL
// You may be able to skip this if you are certain
// your string is safe
const uriSafeIds = encodeURIComponent(commaSeparatedIds);
// Finally, interpolate into your string literal template
// I wasn't sure if the extra $ in your code was intended
// so I left it in.
const url = `get_user/hello?$${uriSafeIds}?admin_form=True`;
const results = await verifiedGet(url, user.user);
In the line const id = user.map((person) => person);, we are looping through the 'user' array and for each iteration, we are just returning the element in that iteration. The 'map' function will return an Array of what will be returned in each iteration. So, basically, we are re-creating the user array in id.
const user = ["u29219", "u302932"];
// 'user' array is re-created here
const id = user.map((person) => person);
console.log("from", id);
const results = `get_user/?${id}admin_form=True`;
If your intention was to form the results string with each element in the array, this would be a way to do it using forEach:
user.forEach((person) => {
const results = `get_user/?${person}$admin_form=True`;
// remaining logic
});
Code-Sandbox
Can make the array unique, then concat or join to get the expected output.
const user = ["u29219", "u302932", "u302932"];
const uniqueItems = [...new Set(user)];
console.log("current uniqueItems", uniqueItems);
let results = "";
uniqueItems.forEach((person) => {
results = results.concat(`get_user/?${person}$admin_form=True `);
});
console.log("current output", results);

ReactJS autoformating while typing field and saving raw data at the sametime

Good evening,
I want to make an autoformat field but I need to store the raw data and the formatted string and I want to auto format user's input in real time, while they are typing. I tried something like:
<input value={this.state.formattedValue} onChange={this.handleOnChange}/>
handleOnChange(e) {
let value = e.target.value
let formattedValue = this.formatValue(value) // my formatting function
this.setState({formattedValue: formattedValue, value: e.target.value})
}
This will work for the first time but then e.target.value will be the formatted value plus the new user's input and I will lose the raw value.
Most of the example on internet take care of just one value (formattedValue) so it's easy but I need both raw and formatted value. I did in the past a version of this where the value was getting formatted on the blur event but in my current case I need it in realtime while typing. If I can get the last character typed by the user I will be able to concatenate it to the values in my state and manage the transformation + the raw value.
Thank you all
You can use two state properties, let's call them rawInput and formattedInput. rawInput will be an array and formattedInput will be a string. You can then get the last character of the input's value and push it to the rawInput array before applying your formatting rules to the value.
Here's an example (note, I'm using hooks and a functional component here, but you can follow the same principle using class based components):
import React, { useState } from "react";
export default function App() {
const [rawInput, setRawInput] = useState([]);
const [formattedInput, setFormattedInput] = useState("");
const handleInput = e => {
const val = e.target.value;
const lastTypedChar = val[val.length - 1];
setRawInput([...rawInput, lastTypedChar]]);
const formatted = val.replace(/[A-Z]/g, c => c.toLowerCase()); // use your own format function here
setFormattedInput(formatted);
};
return (
<div className="App">
<h1>Input</h1>
<input type="text" onChange={handleInput} value={formattedInput} />
<p>Raw Input: {rawInput.join("")}</p>
</div>
);
}
Here's a Codesandbox with a working example.
Note that you will need to apply more logic to handle when a user deletes characters or pastes strings with length greater than 1 into the input if you need that. But the principle of splitting the state is what is important here.

How to pull conditional values from lists inside javascript objects

I have an object, with a list inside each key. What is the best way of returning the key, if the url string matches what's in the array.
For example in the code below, 'entertainment' would be returned as "/video" is in the entertainment array.
const url = "https://example.com/video";
const verticalList = {
autos:[],
entertainment:["/video", "/tech"],
foodanddrink:[],
healthandfitness:[],
homepage:["/home"],
lifestyle:["/ideas", "/executive-life"],
money:["/money-markets",],
news:["/briefing"],
sports:[],
travel:[],
weather:[]
}
You can use Object.entries to get a list of key-value pairs from your object and then run Array.filter to compare values against your url. In the last step you have to run Array.map to retrieve corresponding keys. String.includes can be used to check if one string is a substring of another.
const url = "https://example.com/video";
const verticalList = {
autos:[],
entertainment:["/video", "/tech"],
foodanddrink:[],
healthandfitness:[],
homepage:["/home"],
lifestyle:["/ideas", "/executive-life"],
money:["/money-markets",],
news:["/briefing"],
sports:[],
travel:[],
weather:[]
}
let matches = Object.entries(verticalList)
.filter(([key,value]) => value.some(x => url.includes(x)))
.map(([key,value]) => key);
console.log(matches)

How to I fill a nested array with setState?

What this code is essentially supposed to do is concatenate an empty array object On Click with the add function. Then, I want to fill concatenate each sub array individually depending on the index of the sub Click. So each subList has its own Click to add elements to itself.
The problem is that I keep getting the wrong output when I use setState to update the inner subList. This doesn't happen when I mutate state directly, When I mutate state directly, I get correct results in the console window as intended.
import React, { Component } from 'react';
import './App.css';
//onClick: push an array object onto List
//On Sub Click: Fill the inner arrays individually
class AppTest extends Component {
state = {
List: [
{subList: []}
]
}
this function concatenates an array object each time it is clicked.
add = (event) => {
this.setState(
{List: this.state.List.concat({subList: []})}
);
}
this function grabs the current index of the List and ATTEMPTS to fill each subList individually
based on the index being clicked.
subadd = (i, event) => {
this.setState(
{List: [
{subList: this.state.List[i].subList.concat(0)}
]}
);
//When I mutate state directly, The entire code works as intended: Uncomment below
//to take a look
//this.state.List[i].subList = this.state.List[i].subList.concat(0);
//This is a nested loop that prints the contents of the entire array (including sublists) to the console
for(let i = 0; i < this.state.List.length; i++)
{
console.log(i + "a");
for(let j = 0; j < this.state.List[i].subList.length; j++)
{
console.log(j + "b");
}
}
}
render() {
return (
//My end game is to output input tabs for each element INCLUDING the subList elements in between the main
// List elements
<div>
{this.state.List.map(i => {
return(
<div>
<input value = "Init"/><br />
<div onClick = {this.subadd.bind(this, this.state.List.indexOf(i))}>subClick</div>
</div>
);
})}
<div onClick = {this.add}>Click</div>
</div>
);
}
}
export default AppTest;
/*
All inputs will output the same result: 0a, 0b, 1a, 2a, 3a ...
The length of how many elements are printed is simply the current length of the list.
*/
You can spread the array similar to how you do an Object, as arrays are objects in javascript.
Please find the code here - https://codesandbox.io/s/l4z5z47657
//spread array to new object with the Object keys starting from 0 corresponding to each element in array
let tempArray = { ...this.state.List };
//get the array that we need to change and concat to the sublist
let newSubList = tempArray[i];
newSubList.subList = newSubList.subList.concat(0);
//create new array and update the index with new value . Note the special syntax with [i].
//This is because we have spread an array and not an Object
let toChange = { ...tempArray, [i]: newSubList };
//COnvert the Object to array again
toChange = Object.values(toChange);
this.setState({ List: toChange });
console.log(this.state.List);
This will immutably update the state. You may be able to further reduce the number of lines, but use this as a start.

Categories