I have the following JSON data with more than 2000 city information. In my react app, I could use this JSON data if I can convert them in array. A similar kind of question has been asked already. But I couldn't find similarities. Because I am novice in JS and Reactjs :).
{
"cities": [
{
"lat": "56°09'N",
"lon": "10°13'E",
"city": "Aarhus"
},
{
"lat": "57°09'N",
"lon": "2°07'W",
"city": "Aberdeen"
},
{
"lat": "5°19'N",
"lon": "4°02'W",
"city": "Abidjan"
},
{
"lat": "24°28'N",
"lon": "54°22'E",
"city": "Abu Dhabi"
},
}
Out of this json data how can I make array of city and array of lat and arry of lon?
Updated questions:
Since I am learning Reactjs. I intended to make select with all the available city as follows. Defiantly it is wrong. How can I do it?
<div>
<select name="city" ref={this.props.form.register}>
<option value="">Select...</option>
{
myJson.cities.forEach((eaCity) => {
city = eaCity.city;
value = " "+eaCity.lat; // I need lat and lon later on as string
value += " "+eaCity.lon;
console.log("value: "+value);
console.log("city: "+city);
})
}
<option value={value}>{city}</option> // I will store city, lat, lon after user selection. But I won't show it to the user. So I am trying to have value this way.
</select>
</div>
So you want three separate arrays? Well, let's do that:
const cities = [];
const lats = [];
const longs = [];
You want to extract values from an existing array somehow? Well, let's do that with a forEach:
// Assuming myJson.cities is the array in the "cities" field
myJson.cities.forEach((eaCity) => {
// do something here
});
Hmm, what do we do in our loop? Well, let's just push() a new value into the three arrays we made earlier:
myJson.cities.forEach((eaCity) => {
cities.push(eaCity.city);
lats.push(eaCity.lat);
longs.push(eaCity.long);
});
This all assumes that you already have the JSON data in your app. If you don't, most React app configs (webpack or Parcel) will already be set up to load JSON files:
import myJson from '../path/to/myJson.json';
Let's put it all together:
import myJson from '../path/to/myJson.json';
const cities = [];
const lats = [];
const longs = [];
myJson.cities.forEach((eaCity) => {
cities.push(eaCity.city);
lats.push(eaCity.lat);
longs.push(eaCity.long);
});
Note: this method is a lot more efficient than a .map() because it involves only a single loop (array traverse) instead of unnecessarily traversing the same array three times. :)
Edit: Looking at your update it seems what you actually want to do is map over an array to render something in JSX. That is a different question entirely and not really what the first part of your question is about at all. But it should probably look something like this:
return (
<div>
<select
name="city"
ref={this.props.form.register}
value={this.state.selectedValue}
onChange={(event) => {
this.setState({selectedValue: event.target.value});
}}
>
<option value="">Select...</option>
{
myJson.cities.map((eaCity) => {
return (
<option
key={eaCity.city}
value={`${eaCity.city} ${eaCity.lat} ${eaCity.long}`}
>
{eaCity.city}
</option>
);
})
}
</select>
</div>
);
Note that you have to use .map() and not .forEach() in your render return because .map() returns a new array of some kind (ideally in this case something that can be rendered by React, like an array of components) while .forEach() returns undefined which is not helpful.
If this does not answer your question I really think you should open a new question specific to it and providing more details.
Do you mean something like this?
let cities = cityJsonObj.cities.map(c => c.city);
let lats = cityJsonObj.cities.map(c => c.lat);
let lons = cityJsonObj.cities.map(c => c.lon);
This has nothing to do with React. This is simply mapping an array to different formats. What you'll want to look up for a pure understanding is javascript's Array map method.
Using map you can create a function which will take in each value of the original array, and return a new array which is the result of that function's return value for each value of the old array.
const originalArray = myJson.cities;
const names = originalArray.map(cityObj => cityObj.city);
const latitudes = originalArray.map(cityObj => cityObj.lat);
const longitudes = originalArray.map(cityObj => cityObj.lon);
Things like cityObj => cityObj.city are just arrow functions, which are shorthand ways of writing functions. That would be the same as function(cityObj) { return cityObj.city; }
So every time you run map it's looping through your array, running each value through the given function, and giving you back a new array with the result of those function calls as each value in the new array. That makes it really easy and succinct to get new arrays that are manipulations of other existing arrays.
If processing speed is a concern you also can always simply make 3 empty arrays (names, latitudes, longitudes) and then loop through the original array, adding each value you come across to its appropriate array using myArray.push(valueToAdd). That would mean 1 loop instead of 3 (each time you map it loops through them all). But unless you're having to do this repeatedly (like if this needs to recalculate every time React renders a component) it's usually more worthwhile to keep the code succinct and just use map.
Related
For a Hangman game, I have some topics (eg:cities and animals).
When the user selects one of the topics, the outcome should be one of the chosen topic's random item. eg: London or Zebra etc.
Currently I only have random letter of selected topic.
const cities = ["New York", "London", "Berlin"]
const animals = ["Alligator", "Alpaca", "Zebra"]
const topicsEl = document.querySelector("#topics")
function randomTopic(){
return topicsEl.value[Math.floor(Math.random()*topicsEl.value.length)]
}
topicsEl.addEventListener("change", function(){
console.log(randomTopic());
})
<div class="select">
<label for="topics">Choose a topic:</label>
<select id="topics">
<option value=cities>Cities</option>
<option value=animals>Animals</option>
</select>
</div>
You seem to have issues getting a random value of a list depending on a selection.
Currently, you are selecting a random letter of topicsEl.value instead of a random element of the associated topic's list.
You need to determine the list to choose from depending on topicsEl.value. Dynamically this can be achieved if that value can be used as a key (e.g. for a dictionary), but this can also be done statically.
But doing it statically would result in duplicate code, e.g. in an if-else-if ladder growing for each new topics list:
function randomTopic() {
if (topicsEl.value === "cities") {
// ... (use citites)
} else if (topicsEl.value === "animals") {
// ... (use animals)
} // Etc. for every new topic
}
Doing it dynamically allows for abstracting away the list selection, keeping the function simple. As suggested before, a dictionary can be used for this.
For example, your dictionary's properties could each be a topic list, and your option values should then match their corresponding property's name:
const topics = {
cities: [/*...*/],
animals: [/*...*/]
};
const topicsEl = document.querySelector("#topics");
function randomTopic() {
const list = topics[topicsEl.value];
// ...
}
Selecting a random item of that list works analogous to how you are currently selecting a random letter:
function randomTopic() {
const list = topics[topicsEl.value];
return list[Math.floor(Math.random() * list.length)];
}
Personally, I find such random selections more readable if the index generation is in a separate function. Example:
const edibles = ["cheese", "ham", "bread", "banana", "peanuts"];
console.log(randomEdible());
function randomEdible() {
return edibles[randomInt(0, edibles.length - 1)];
}
function randomInt(max = 100, min = 0) {
if (max < min) {
[max, min] = [min, max]; // Swap
}
return Math.floor(Math.random() * (max - min + 1) + min); // +1 to include max
}
In your existing code, topicsEl.value is going to be the string "cities" or the string "animals" (because those are the value of each of the options in your <select> box.). These are not the global variables you defined in your javascript, they're just strings contained in the HTML.
You then, in randomTopic(), access that string as an array, which Javascript interprets as you wanting two treat it as an array of the characters within that string. This is why you're getting a random letter from the word: "animals"[0] is the letter a, "animals"[1] is the letter n, and so on.
What you're trying to do is choose a random item from the array variables you've named "cities" and "animals", but your functions don't try to touch those variables, they're only acting on the strings contained in the DOM.
So you need to add a step, to get from the string value from the <select> to the array you're trying to access.
You've defined the two arrays as global variables; in theory those can be accessed as window.cities or window.animals, so you could do window[topicsEl.value] which would return the array you're trying to access.... it's not great practice to depend on the window global, though, so I'd encourage you to switch that pair of separate variables in to an object for easier access:
const topics = {
cities: ["New York", "London", "Berlin"],
animals: ["Alligator", "Alpaca", "Zebra"]
}
const topicsEl = document.querySelector("#topics")
function randomTopic() {
// find the desired array:
let topicArr = topics[topicsEl.value]
// return a random element from that array:
return topicArr[Math.floor(Math.random() * topicArr.length)]
}
topicsEl.addEventListener("change", function() {
console.log(randomTopic());
})
<div class="select">
<label for="topics">Choose a topic:</label>
<select id="topics">
<option value=cities>Cities</option>
<option value=animals>Animals</option>
</select>
</div>
I have three models (I am using Vue/Vuex-ORM on the frontend). Category, CategoryItem and Item.
I'm able to fetch an array of categories, and within each category is an array of items. An intermediate join model defines the relationships of these two models, and I am able to access as such:
// array of categories, each category has array of items
const categories = Category.query().where('pack_id', this.selectedPack.id).with('items').get();
categories.map(category => {
category.items.forEach(item => {
console.log('item.pivot: ', item.pivot); // pivot refers to join model
// how to order items based on item.pivot?
})
})
Within the .forEach, I can access the join model with item.pivot. What I am looking to do however, is sort each category's items based on item.pivot.position.
I started going down a path where the first line inside of the .map I defined a new empty array, and would then theoretically push in a new value based on whether the position was higher or lower, but I couldn't quite wrap my head around how to accomplish this.
Thanks!
Well just my luck. A half hour after posting this question, I figure it out! Here was what I did, in case anyone is curious.
categories() {
const categories = Category.query().where('pack_id', this.selectedPack.id).with('items').get();
categories.forEach(category => category.items.sort(this.compare));
return cats;
}
compare(a, b) {
let comparison = 0;
if (a.pivot.position > b.pivot.position) comparison = 1;
else if (a.pivot.position < b.pivot.position) comparison = -1;
return comparison;
},
Currently, I have a huge JavaScript array where each element is like this:
[{"open":235.86,
"high":247.13,
"low":231.5,
"close":244.1,
"volume":55025735,
"date":"2019-05-01T21:00:00.000Z"}
...
I need to remove everything except the price after high. What is the most efficient way I can do this?
I've tried popping the individual elements, but I can't help but feel as if there is a more efficient/easier way to do this.
So hopefully the ending array would just be [235.86].
The below code should work. It's efficient enough :D
for (i in arrayName){
// Loops through array
delete arrayName[i].high
delete arrayName[i].low
delete arrayName[i].close
delete arrayName[i].volume
delete arrayName[i].date
// Deletes unwanted properties
}
console.log(arrayName)
// Print output
One potential solution would be to map the array to a new array like so:
const yourArray = [
{"open":235.86, "high":247.13, "low":231.5, "close":244.1, "volume":55025735},
{"open":257.52, "high":234.53, "low":220.2, "close":274.1, "volume":23534060},
]
const mappedArray = yourArray.map(el => el.open);
// mappedArray = [235.86, 257.52]
Check out the MDN documentation for the map method, Array.prototype.map()
Note: The above example uses ECMAScript 6 arrow functions and implicit returns. It is functionally equivalent to:
const yourArray = [
{"open":235.86, "high":247.13, "low":231.5, "close":244.1, "volume":55025735},
{"open":257.52, "high":234.53, "low":220.2, "close":274.1, "volume":23534060},
]
const mappedArray = yourArray.map(function(el){
return el.open
});
You can use reduce for this scenario. Example
var temp = [{"open":235.86, "high":247.13, "low":231.5, "close":244.1, "volume":55025735, "date":"2019-05-01T21:00:00.000Z"}];
var highValArray = temp.reduce((arr, t) => {
return arr.concat(t['high']);
}, []);
You can learn more about reduce function at the MDN website.
This should work:
your_array.map((item) => {
return item.high
})
I just started learning RxJS. One thing I have tried to do without much luck is, figuring out how to search/filter a Subject, or create an observed array that I can search on.
I've tried piping and filtering a Subject and BehaviorSubject, but the values in the predicate are RxJS specific.
From what I've read on various posts, the way is to observe an array to use a Subject.
I can easily have two variables, one array and the Subject, and search the array. But I'd like to accomplish this one variable.
In Knockout its possible to search an observed array.
Is this possible in RxJS?
Thanks in advance.
Example:
layers: Rx.Subject<any> = new Rx.Subject<any>();
toggleLayer (layerId, visible) {
//find layer we need to toggle
// How do I search the subject to get the values added in next()?
// tried from(this.layers), pipe does not fire
const source = of(this.layers);
const example = source.pipe(filter((val, index) => {
//val is subject, and only iterates once, even if more than one value in subject
// tslint:disable-next-line:no-debugger
debugger;
return false;
}));
const sub = example.subscribe((val) => {
// tslint:disable-next-line:no-debugger
debugger;
});
}
private addLayer = (layerName, layerObj, layerType) => {
// component class is subscribed to layers subject. Update UI when layer is added
this.layers.next({
layerId: this.layerId,
name: `${layerName}_${this.layerId}`,
layerObj: layerObj,
visible: true,
layerType: layerType
});
}
I'm not 100% clear on the specifics of your ask, but maybe this example will help you.
const filterSubject = new BehaviorSubject<string>('b');
const dataSubject = new BehaviorSubject<string[]>(['foo', 'bar', 'baz', 'bat']);
const dataObservable = combineLatest(filterSubject, dataSubject).pipe(
// given an array of values in the order the observables were presented
map(([filterVal, data]) => data.filter(d => d.indexOf(filterVal) >= 0))
);
dataObservable.subscribe(arr => console.log(arr.join(',')));
// bar, baz, bat
Using combineLatest, you can have the value in dataObservable updated whenever either your filter value or your data array changes.
I am having a difficult time getting Bootstraps typeahead in angular 5 working would appreciate some advice. The problem I have is that I don't know how to set the input field to equal the city + state for exaple "New york, NY" in bootstraps search method example. I am new to Typescript and the new fat arrow feature in JavaScript any help would be greatly appreciated.
model array of objects
public model: any;
example of data that I am getting
{
"city":"New York",
"latitude":40.7127837,
"longitude":-74.0059413,
"state":"New York",
"stateCode":"NY"
}
Search method here I am trying to set the location items to filter 'city,'+'state'
search = (text$: Observable<string>) => text$
.debounceTime(200)
.distinctUntilChanged()
.map(term => term.length < 2 ? [] : this.locationItems.filter(item => item.city.toLowerCase().indexOf(term.toLowerCase()) > -1).slice(0, 10));
The arrow function inside the last map in your chain needs to map (transform) the value of the string the user has typed into the search box to an array of items that will be presente to the user as suggestions.
You start nice, by askin if term is only one character long (or an empty string) and do nt even run the search, imediatelly returning empty array. For the other part, you need to find which items you want to present to the user.
This part depends on your business logic, but I assume that you want user to be searching by either state or stateCode? Anyway, this part is your business logic and you can change and improve it according to your busniess model. I'm giving a very simply function in the code below.
// util function
// returns true if the "term" can be used to match the "item" from your data
// change this according to your business logic
function matches(term: string, item: Item): boolean {
return item.state.toLowerCase().includes(term.toLowerCase())
|| item.stateCode.toLowerCase().includes(term.toLowerCase())
}
The lambda in the last map can be like this.
term => {
if (term.length < 2) {
return []
}
// arary of matching results
const matching = this.filter(item => matches(term, item))
// now we transform this to the format you specified
const formatted = matching.map(item => `${item.state}, ${item.stateCode}`)
return formatted
// you can also .slice(0, 10) here like you did in your example to keep the number of suggestions no more than 10
}