javascript find() in nested array - javascript

I'm trying to build a website with REACT. In the homepage you have 2 buttons, europe and usa.
Let's say you click europe. Then you see a list of all the countries in europe.
And when you click a country, you should see a list of CITIES in that country.
The question is, how can I access the items inside "cities"?.
const DATA = [
{
id: 1,
title: "EUROPE",
countries: [
{
id: 1,
country: "france",
cities: [
{
id: 1,
city: "paris"
},
{
id: 2,
city: "toulouse"
}
];
// so at homepage, you click "europe", and on the second page i got this:
const StateCard = () => {
const { title } = useParams();
const selectedData = DATA.find( d => d.title === title);
return(
<div className="main">
{selectedData &&
selectedData.countries.map((item, id) => {
return (
<div className="card-container" >
<Link key={id} to={`${title}/${item.country}`}> {item.country} </Link>
</div>
);
})}
</div>
useParams gives us back the title that added to the URL after the first click,
which is "europe".
selectedData gives us back the items inside "europe":
{id: 1, title: "EUROPE", countries: Array(1)}
and now the screen shows "france". you clicked france, and now i wanna show the 2 cities inside.
all i got is:
const { country } = useParams();
which gives us "france".
but i dont know how to access the cities inside.
i tried to play with DATA.countries.find(), but whatever i put after DATA. gives me
"TypeError: Cannot read property 'find' of undefined".
sorry its so long thanks guys!

Each type (continents, countries, cities) is an array. find won't work on DATA.countries because countries is a property of whatever continent object you select.
It may help you to divide up your data collections using a series of methods. getContinents gets the data as an argument, and the value of title. getCountries receives the array returned by getContinents - as well as the value of country - and returns its own array of countries, and then getCities maps over that data to return the city names.
This way you maintain a series of data collections, and the code is easier to maintain.
const data = [{"id":1,"title":"EUROPE","countries":[{"id":1,"country":"france","cities":[{"id":1,"city":"paris"},{"id":2,"city":"toulouse"}]}]}];
const title = 'EUROPE';
const country = 'france';
const getContinents = (data, val) => data.find(obj => obj.title === title)
const getCountries = (data, val) => data.countries.find(obj => obj.country === val)
const getCities = (data) => data.cities.map(obj => obj.city);
// Pass in the data and the value of title
const continents = getContinents(data, title);
// Use the array returned from `getContinents` and the country value
const countries = getCountries(continents, country);
// Use the array returned from `getCountries`
const cities = getCities(countries);
console.log(cities);

You can first find the Country and then on countriesResult you can find citiesResult and then can find cities from citiesResult.cities.
You're getting the error **TypeError: Cannot read property 'find' of undefined** because
what you're doing is DATA.countries.find(). Data is an array so you can't use .countries on it. You have to find the country using find or use index.
const DATA = [{
id: 1,
title: "EUROPE",
countries: [{
id: 1,
country: "france",
cities: [{
id: 1,
city: "paris",
},
{
id: 2,
city: "toulouse",
},
],
}, ],
}, ];
const title = "EUROPE";
const country = "france";
const countriesResult = DATA.find((d) => d.title === title);
const citiesResult = countriesResult.countries.find(
(c) => c.country === country
);
const result = citiesResult.cities.map((c) => c.city);
console.log(result);

DATA is an array of objects. Each item in DATA have countries array. So, you have to use find() on specific item in DATA array, and not on DATA array iteself. For example, you can do this:
DATA[0].countries.find()

Related

mapping through the array of arrays ReactJS / JS

I have the following code.
I have this array of array data.
const data = [
[
{
city: "Phnom Penh",
country: "KH"
},
{
city: "Tirana",
country: "AL"
},
{
city: "Andorra la Vella",
country: "AD"
}
],
[
{
city: "Mariehamn",
country: "AX"
}
],
[]
];
I am trying to print all city in the new variable and then want to show in select
const cities = data.map((el) => el).map((el, idx) => el[idx]?.city);
<select>
{cities.map((el) => (
<option value={el} key={idx}>
{el}
</option>)}
</select>
But I am getting only first city.
The output now is
(3) ["Phnom Penh", undefined, undefined]
But the output should be
(4) ["Phnom Penh", "Tirana", "Andorra la Vella", "Mariehamn"]
Please help me to correct my code.
Thanks.
I fixed your code
For an easy way to understand what's going on, I divided it into two variables.
const a = data.flatMap((num) => num);
const b = a.map((el) => el.city);
And then you can show it in your select tag
const cities = data.flat().map(item => item.city);
The flat() method creates a new array with all sub-array elements concatenated into it recursively up to the specified depth.
The Javascript map() method in JavaScript creates an array by calling a specific function on each element present in the parent array. It is a non-mutating method. Generally, the map() method is used to iterate over an array and calling function on every element of the array.
console.log(cities);
// Output: ["Phnom Penh", "Tirana", "Andorra la Vella", "Mariehamn"]
Since flat may not be available on every environment I am providing with an alternative solution:
let cities = [];
data.forEach(el=> {
el.forEach(element => {
cities.push(element.city);
});
});

How can I add keys in the array, and generate input fields with them?

The user has the possibility to enter his name. I get the name in a array. Example: ["Dilan", "Chris", "Robert"]
So what I'm trying to do is to get the name from the user, but also another value inside the array.
Example:
initialName = {
name: "",
active: "",
};
Also, I need the initalName to be inside an array.
This is what I have tried by far
const initialName = {
name: "",
active: ""
};
export default function AddingNames() {
const [names, setNames] = useState([initialName ]);
const handleAddClick = () => {
setLanguages([...names, initialName]);
};
const handleItemChanged = (event, index) => {
const value = event.target.value;
const list = [...initialName];
list[index] = value;
setNames(list);
console.log(names);
};
And this error is showed: TypeError initialName is not iterable
What I want to get from the user its just the name value in the initialName.
How can I make it work?
you can only use ... in case of iterable variable, in this case initialName is not iterable since its object
I suppose you are trying to achieve array that looks something like this:
list =
[
{name: "boo", active: "foo"},
{name: "john", active: "doe"}
]
In this case what you could do is
list[index] = {name: value, active: ""};
setNames(list)
So initial name is not iterable here as you are trying to do in usestate([]) because initial name is an object not an array. If you had an array say:
let users = [{
name: "",
active: "",
}]
Then you could iterate over the users array and access the name of a user by
let variable = user[0].name

ReactJS realtime search filter of array of arrays

I'm trying to create a realtime filtering search for a long list of items, items are in the form of an array containing arrays with a string and a object, as:
const items = [
["cats", [
{name: "name", img: "img/img.jpg"},
{name: "name2", img: "img2/img.jpg"},
...
]],
["dogs", [
{name: "name", img: "img/img.jpg"},
{name: "name2", img: "img2/img.jpg"},
...
]],
...
]
and I'm trying to filter it by name values.
What I tried is:
export default function Page({ items }) {
const [searchTerm, setSearchTerm] = React.useState("");
const [searchResults, setSearchResults] = React.useState(items);
const handleChange = event => {
setSearchTerm(event.target.value);
}
useEffect(() => {
var f=0, results = []
while (f < items.length){
if(items[f][1].includes(searchTerm)){
results.push(result)
}
f++
}
setSearchResults(results);
}, [searchTerm]);
return (
<div>
<input
type="text"
placeholder="Search"
value={searchTerm}
onChange={handleChange}
/>
{searchResults.map((cl, index) => (
<div key={index}>
<h2>{cl[0]}</h2>
<ScrollContainer>
{cl[1].map((item, i) => (
<h3>{item.name}</h1>
))}
</ScrollContainer>
</div>
))}
but it throws error:
ChunkLoadError: Loading chunk 0 failed.
First of all, please fix your data model. when it is numerable so why you used Array of Array, turn it like this:
const items = [
{ name: "name", img: "img/img.jpg", type: "cat" },
{ name: "name2", img: "img2/img.jpg", type: "cat" },
...
{ name: "name", img: "img/img.jpg", type: "dog" },
{ name: "name2", img: "img2/img.jpg", type: "dog" },
...
]
And about your real-time search, use fuse.js, you can add it in your project and with simple config it filtered for your, specially fuse.js is very comfortable with the above data model. just try it. you will be happy with it.
I'd suggest to fix your data like what AmerllicA said but if you have a current data format like that you can convert it like this.
const items = [
["cats", [
{name: "name", img: "img/img.jpg"},
{name: "name2", img: "img2/img.jpg"},
]],
]
const object = Object.fromEntries(items);
this will convert your data into like this:
{
"cats":[{"name":"name","img":"img/img.jpg"},{"name":"name2","img":"img2/img.jpg"}]
}
so if you would like to find the cats or dogs
you can do is:
const searchTerm = 'cats'
const foundKey = Object.keys(object).find(key => key === searchTerm)
const foundObject = object[foundKey];
The output of foundObject would be:
"[{"name":"name","img":"img/img.jpg"},{"name":"name2","img":"img2/img.jpg"}]"
and if you want to find the specific name for the cat would be like this
const searchNameOfFoundObject = 'name';
const findNameOfFoundObject = foundObject.find(value => value.name === searchNameOfFoundObject)
then the output of the findNameOfFoundObject would be:
{"name":"name","img":"img/img.jpg"}
But I suggest that you do the searching on the backend if this is a large data this is going to be a burden on your frontend this will make your application slow. You should just send the searchTerm on an api and the data that you will need base on the searchTerm will be the api response this will be handled much more faster on the backend and your frontend react application will be more optimize and user friendly.

Merge Data from different Queries without duplicates

I am getting data from three different queries via Api. I want data to be merged without the duplicate data.
This is my function where i am merging the data:
getStaffCount(data) {
if (data == null || data.results === null )
return [];
else
return data.results.StaffCount.map(m => ({ Name: m.Name, Accounts: m.Accounts })).
concat(data.results.RepProviderAccount.map(m => ({ Name: m.Name, Accnt: m.Accnt }))).
concat( data.results.ProviderAccount.map(m => ({ Name: m.Name, Account: m.Account })));
}
This is my table:
<PowerTable Data={{ rows: this.getStaffCount(this.props.GridData) }} rowsPerPage={5} orderBy="Name" order="asc" >
<PowerColumn id='Name' columnName='Name' numeric={false} disablePadding={false} label='Profile Name' width={100}>
</PowerColumn>
<PowerColumn id='Accounts' columnName='Accounts' numeric={false} disablePadding={false} label='Staff Accounts' width={100}>
</PowerColumn>
<PowerColumn id='Account' columnName='Account' numeric={false} disablePadding={false} label='Provider Account' width={100} >
</PowerColumn>
<PowerColumn id='Accnt' columnName='Accnt' numeric={false} disablePadding={false} label='Rep Provider Account' width={100} >
</PowerColumn>
</PowerTable>
So in the above image same Profile Name(CNX MSL Platform) is coming twice. So is there any way i can merged those rows?
I want the Output in this way:
Profile Name Staff Provider Rep Provider
Cnx MSl Platform 2 1
Cnx Specilaity sales Platform 7 22
Data:
As an object
if the data is an object the easy way to do that is the spread opperator
const combinedData = {
...dataSrc1,
...dataSrc2,
...dataSrc3,
}
All matching keys will be overwritten by the previous
As an array
It's a bit more complex. Assuming your object has a unique id (or any value to identify 2 as the same item) you can use a Set since they can only have unique values.
const array = [
...dataSrc1,
...dataSrc2,
...dataSrc3,
]
const unique = [...new Set(array.map(item => item.id))];
Your answer to my question about what the data looks like and how to group them didn't make any sense, neither did you answer Joe just showed the json data and tell him where the data comes from instead of what it is.
So I assume you group by Name and Account is ignored. You can group them in the following way:
const data = {
results: {
StaffCount: [
{
Name: 'a',
Accounts: 2,
},
{
Name: 'b',
Accounts: 20,
},
],
RepProviderAccount: [
{
Name: 'a',
Accnt: 3,
},
],
ProviderAccount: [
{
Name: 'a',
Account: 1,
},
],
},
};
const grouped = [
...data.results.StaffCount,
...data.results.RepProviderAccount,
...data.results.ProviderAccount,
].reduce((result, item) => {
const {
Name,
Account = 0,
Accounts = 0,
Accnt = 0,
} = item;
const existing = result.get(item.Name) || {
Name,
Account: 0,
Accounts: 0,
Accnt: 0,
};
existing.Account += Account;
existing.Accounts += Accounts;
existing.Accnt += Accnt;
return result.set(Name, existing);
}, new Map());
console.log([...grouped.values()]);
In case this doesn't work for you can you please update your question and provide code as in my answer with the expected input and output? You can respond to this answer and I'll have a look at your question again.
This may actually be an xy problem, you are fetching 3 data sources and then trying to group and sum them but maybe you can just get 1 data source and try salesforce to group and sum them in the query. I don't know enough about salesforce but maybe you can ask another question tagging it with soql if it's possible to just get the data grouped and summed.

Javascript using filter/includes on an array of objects

What is the best way to filter out data that exists within an object?
I was able to do use the below code when data was just an array of values but now I need to filter out any data where the item.QID exists in my array of objects.
Data Obj:
var data = [{
QID: 'ABC123',
Name: 'Joe'
},
{
QID: 'DEF456',
Name: 'Bob
}]
Snippet:
// I don't want to include data if this QID is in my object
this.employees = emp.filter(item =>!this.data.includes(item.QID));
From what I understand, includes only works on an array so I need to treat all of the QID values in my object as an array.
Desired Outcome: (assuming item.QID = ABC123)
this.employees = emp.filter(item =>!this.data.includes('ABC123'));
Result:
var data = [{
QID: 'DEF456',
Name: 'Bob'
}]
UPDATE:
Apologies, I left some things a little unclear trying to only include the necessary stuff.
// People Search
this.peopleSearchSub = this.typeahead
.distinctUntilChanged()
.debounceTime(200)
.switchMap(term => this._mapsService.loadEmployees(term))
.subscribe(emp => {
// Exclude all of the current owners
this.employees = emp.filter((item) => item.QID !== this.data.QID);
}, (err) => {
this.employees = [];
});
The above code is what I am working with. data is an object of users I want to exclude from my type-ahead results by filtering them out.
The question is a little ambiguous, but my understanding (correct me if I'm wrong), is that you want to remove all items from a list emp that have the same QID as any item in another list data?
If that's the case, try:
this.employees = emp.filter(item => !this.data.some(d => d.QID === item.QID))
some is an array method that returns true if it's callback is true for any of the arrays elements. So in this case, some(d => d.QID === item.QID) would be true if ANY of the elements of the list data have the same QID as item.
Try Object#hasOwnProperty()
this.employees = emp.filter(item =>item.hasOwnProperty('QID'));
You can use a for ... in to loop through and filter out what you want:
const data = [{
QID: 'ABC123',
Name: 'Joe'
},
{
QID: 'DEF456',
Name: 'Bob'
}]
let newData = [];
let filterValue = 'ABC123';
for (let value in data) {
if (data[value].QID !== filterValue) {
newData.push(data[value]);
}
}
newData will be your new filtered array in this case
You can use an es6 .filter for that. I also added a couple of elements showing the filtered list and an input to allow changing of the filtered value. This list will update on the click of the button.
const data = [{
QID: 'ABC123',
Name: 'Joe'
},
{
QID: 'DEF456',
Name: 'Bob'
}]
displayData(data);
function displayData(arr) {
let str = '';
document.getElementById('filterList').innerHTML = '';
arr.forEach((i) => { str += "<li>" + i.QID + ": " + i.Name + "</li>"})
document.getElementById('filterList').innerHTML = str;
}
function filterData() {
let filterValue = document.getElementById('filterInput').value;
filterText (filterValue);
}
function filterText (filterValue) {
let newArr = data.filter((n) => n.QID !== filterValue);
displayData(newArr)
}
<input id="filterInput" type="text" value="ABC123" />
<button type ="button" onclick="filterData()">Filter</button>
<hr/>
<ul id="filterList"><ul>

Categories