I'm looking for a proper way to merge two array to add into my tracking properties.
This is my function for tracking :
const trackEvent = (user, title, properties = {}) => {
console.log({
userId: user.cuid,
event: title,
properties: { ...properties }
});
};
Then, I've got two examples of two properties I want to merge :
export const testProperties = ({ "Color": "Black"});
export const testProperties2 = ({ "Amount": "200"});
Then, when I call my function :
trackEvent(user, "Mon titre", testProperties );
I didn't want the merge array to have an index but to merged. I try to concat them without success.
If you want to merge the two objects into a single one, you should do it before you pass it to the trackEvent() function. This can be done by creating a new object and spreading the two objects into it, i.e.:
trackEvent(user, "Mon titre", { ...testProperties, ...testProperties2 });
Related
I'm trying to display game information from the Steam API in a React Native Flatlist. I'm new to React and JSX, so a lot of what I'm reading doesn't make sense.
I want the Flatlist to display a list of game titles owned by a particular account. The data returned from Steam's API call (via fetch) looks like this:
{
"response": {
"game_count": 69,
"games": [
{
"appid": 220,
"name": "Half-Life 2",
"playtime_forever": 24,
"img_icon_url": "fcfb366051782b8ebf2aa297f3b746395858cb62",
"img_logo_url": "e4ad9cf1b7dc8475c1118625daf9abd4bdcbcad0",
"has_community_visible_stats": true,
"playtime_windows_forever": 0,
"playtime_mac_forever": 0,
"playtime_linux_forever": 0
},
{
"appid": 320,
"name": "Half-Life 2: Deathmatch",
"playtime_forever": 0,
"img_icon_url": "795e85364189511f4990861b578084deef086cb1",
"img_logo_url": "6dd9f66771300f2252d411e50739a1ceae9e5b30",
"has_community_visible_stats": true,
"playtime_windows_forever": 0,
"playtime_mac_forever": 0,
"playtime_linux_forever": 0
},
and so on. Since I'm trying to display a list of games by name, the name attribute is the only one I need.
The data lists each game as an anonymous object, so I can't access the properties within each game using dot notation like I normally would. I tried using a for loop to iterate through them, but that doesn't work either. From my research, it seems like people normally use an Array.map for this kind of thing, but I'm unclear if that can be used with Objects.
Another problem I've encountered is the Flatlist keyExtractor property. I know it's supposed to be an anonymous function that returns some unique index or property about each Flatlist item, for the purpose of making the structure more efficient and to allow it to track updates to the list. However, I have no idea how to create this function myself. I think the appid field from the JSON data would be a good candidate, but I'm not sure how to get that into the keyExtractor function.
So, to put it as a question: How would I go about displaying data from a JSON object containing anonymous sub-objects in a Flatlist, and how would I populate the keyExtractor of that list with a different data entry (the appid from that list?
Below is my starting code:
import React, {Component} from 'react';
import {FlatList, Stylesheet, Text, View} from 'react-native';
export default class App extends Component {
state = {
dataset: []
};
componentWillMount() {
this.fetchData();
}
fetchData = async () => {
const response = await fetch("<API URL>");
const json = await response.json();
//const data = json.map((item) => item.games.name);
var key = 0;
const data = json[games][0][name];
this.setState({ dataset: data });
}
render() {
console.log(this.state.dataset);
return (
<View>
<FlatList
data={this.state.dataset}
keyExtractor={(x, i) => i} //I have no idea what this does, or if it makes sense here.
//Where do x and i come from? (I got this from a tutorial video
//and this was glossed over)
renderItem={({ item }) => //Where does item come from?
<Text>
{item}
</Text>
}
/>
</View>
);
}
}
Alright, it seems you're having a few minor problems with understanding how FlatList works. Let me break it down for you.
Let's start with the Steam API request. In your example, you're first declaring dataset as an empty array in your state, then trying to update it with the result of a network request which is the way to go. The problem is, when you do json['games'][0]['name'] you're accessing the first item (index 0) of the games array and getting its name property and then setting that name as your dataset. Although you forgot the quotes around property names, it won't work. What you need to do instead is something like this:
fetchAllGames = async () => {
const steamResponse = await fetch("<API URL>");
const json = await steamResponse.json();
// We get all the games back from Steam in the form of an array
this.setState({ games : json.games });
}
We're now correctly updating the array inside our state with the data from the games array.
Let's move on to the keyExtractor and renderItem functions. The keyExtractor function is used to tell React about a unique identifier for each of your list items. In this case, this would be the appid property of a game. React then uses this unique ID to differentiate between items and determine which ones need updating. This function provides you with two parameters, namely the actual item and its index. Using these, we can then do something like this:
keyExtractor = (item, index) => {
return item.appid.toString();
}
We're now returning the appid property as a string (which is the type React expects key to be).
The renderItem function is a bit different, React is providing you with a parameter which contains your item plus a lot of other properties. Since we're only interested in the actual item, we're destructuring it using brackets like so: { item }. This is a technique commonly used in JavaScript to "extract" properties from objects. It is normally used like this:
const testObj = {
name : "John",
surname : "Doe"
}
const { name, surname } = testObj;
This way, you can directly refer to name and surname as if they were independent variables. Another way of doing this would be:
const testObj = {
name : "John",
surname : "Doe"
}
const name = testObj.name;
const surname = testObj.surname;
I hope this cleared some of the questions you might've been asking yourself! Here's the complete working code below. You may notice I moved some inline functions to class members, this is just a performance optimization to prevent the functions from being recreated on every render, you can ignore that.
import React, { Component } from 'react';
import { FlatList, Text } from 'react-native';
export default class App extends Component {
state = {
games : []
};
componentDidMount() {
this.fetchAllGames();
}
fetchAllGames = async () => {
const steamResponse = await fetch("<API URL>");
const json = await steamResponse.json();
// We get all the games back from Steam in the form of an array
this.setState({ games : json.response.games });
}
keyExtractor = (item, index) => {
return item.appid.toString();
}
renderItem = ({item}) => {
return (
<Text>{item.name}</Text>
);
}
render() {
return (
<FlatList
data={this.state.games}
keyExtractor={this.keyExtractor}
renderItem={this.renderItem} />
);
}
}
EDIT #1 - As pointed out by the OP, I made a typo and corrected it. I also changed the JSON object to reflect the response property.
I'm new to React.
I have the code below with a function, but when I run it, it returns an error:
TypeError: renderJson[item.node] is not a function.
How can I fix the renderJson function?
export const readItem = item => {
printlog(item);
return renderJson[item.node](item);
};
const renderJson = {
"heading": item => <h1>{item.map(item => readItem(item))}</h1>
};
If you're trying to create a single React functional component that takes a JSON, and outputs the items in the JSON as a header, it would be more like this:
// If you're getting this JSON from an external source using something like a GET request, put the request inside a "useEffect()" hook
const myJson = {
"heading": ["My First Header", "My Second Header"]
};
export const Header = () => {
console.log(myJson);
return <h1>{myJson.heading.map(header => header}</h1>
};
I apologize if this is a misinterpretation of your question. If it is, any additional details would be helpful.
In our angular application we have the current situation, that an existing JSON file get's bigger and bigger with upcoming tasks and the idea is to split it up into multiple JSON files and import them at the right position if needed.
The objects are nested and the idea is, to split up the last object into a separate file and import it later. How do I achieve this? By looping through all nested objects until I find my property?
General question, is this process basically considered as an import or merge?
Is this good practice or should we rethink the process to avoid redundancy?
{
"name":"a",
"value1":"b",
"properties": {
"name":"aa",
"value1":"bb",
"properties": {
"name":"aaa",
"value1":"bbb",
"properties": {
//import here
}
I'm new in the business and also 1st time posting on stackoverflow, so please be gentle.
That's called flattening and it's a rather common issue.
To resolve it, use a recursive function (a function that calls itself).
I will give you the flattener from top to bottom, feel free to try starting from the bottom by yourself !
N.B. : (This is a rather advanced syntax, if you don't understand it I can explain it to you or simplify it)
const data = {
id: 1,
child: {
id: 2,
child: {
id: 3,
child: {
id: 4,
child: {
id: 5,
}
}
}
}
};
const flattened = [];
function flatten(item, target) {
const { child, ...toPush } = item;
target.push(toPush);
child && flatten(child, target);
}
flatten(data, flattened);
console.log(flattened);
You can then save every item of the array. I suggest you use a library for that, such as FileSaver, to create files and download them.
EDIT
Simplified :
const data = {
id: 1,
child: {
id: 2,
child: {
id: 3,
child: {
id: 4,
child: {
id: 5,
}
}
}
}
};
const flattened = [];
function flatten(item, target) {
const toPush = { id: item.id };
const child = item.child;
target.push(toPush);
if (child) flatten(child, target);
}
flatten(data, flattened);
console.log(flattened);
Explanation
You start by creating a function : this function will accept an item, and a target as params.
In the function, you must add the item into the array, and do so with all of the child tree.
To do that, you create an empty array, and use .push with the item minus its child (in my case, only the ID)
Once pushed, you have to rerun this function, but the item becomes the child of the previous item.
You secure the function by using a condition that says it won't do anything if the child isn't defined.
With this, you get a flattened array, as seen in the console log.
For the "advanced syntax", I used destructuring assignement and logical operators, as explained here
Following the advice given on one of my other questions (Get a list of all objects from loaded model), I added this code on my application to retrieve all properties from all objects:
function onGeometryLoadedEvent(viewer) {
var dbIds = Object.keys(
viewer.model.getData().instanceTree.nodeAccess.dbIdToIndex
);
this.viewer.model.getBulkProperties(
dbIds,
{
propFilter: false,
ignoreHidden: true
},
(objects) => console.log(objects),
() => console.log('error')
)
}
It correctly shows all the objects. The problem is it takes A LOT of time to complete (+1 minute), even for a model with just 10,000 objects.
Is this normal?
I really need the list of objects with their categories, I have to sort them after getting them to present a list of all the available categories and properties to the user.
I know I can use the Model Derivatives API, but I'd like to avoid it if possible.
Note that when you list all elements on the model it will include those that are not visible, like categories. If you need only elements on the model, then you need the leaf of the model. This article described it and the source code is below:
function getAllLeafComponents(viewer, callback) {
var cbCount = 0; // count pending callbacks
var components = []; // store the results
var tree; // the instance tree
function getLeafComponentsRec(parent) {
cbCount++;
if (tree.getChildCount(parent) != 0) {
tree.enumNodeChildren(parent, function (children) {
getLeafComponentsRec(children);
}, false);
} else {
components.push(parent);
}
if (--cbCount == 0) callback(components);
}
viewer.getObjectTree(function (objectTree) {
tree = objectTree;
var allLeafComponents = getLeafComponentsRec(tree.getRootId());
});
}
Now with the .getBulkProperties you can specify which property you want, so something like Category, right? So specify that, as shown at this article and used with the above .getAllLeafComponent function:
getAllLeafComponents(viewer, function (dbIds) {
viewer.model.getBulkProperties(dbIds, ['Category'],
function(elements){
// ToDo here
// this will only include leaf with Category property
})
})
And I would expect this to be faster than your original code, as I'm filtering elements and properties.
how do i use a for each loop to create accordian containers for each data returned from json file using ajax ?
i have tried this ! is it the way to it ?
dojo.xhrGet({
url:"json/"file_Name".json",
handleAs: "json",
timeout: 10000,
load: function(response,details){
container(response)},
error: function(error_msg,details){
container(error_msg, details);
}
});
//how do i use the json file to add data to the array arrFruit and then create dijit accordian container for every data in the array//
container = function(array, domConstruct) {
var arrFruit = ["apples", "kiwis", "pineapples"];
array.forEach(arrFruit, function(item, i){
domConstruct.create("li", {innerHTML: i+1+". "+item}, "properties");
});
};
//the response data from my json file is:-
[
{
"itemId": 1234,
"Name": "Name",
}]
I would suggest you re-write it into leveraging the ItemFileReadStore. The Store is a data container in which you can pull out items by their id. This means that your json needs to be changed slightly with description of what is identifier and - if any - which is the children attribute keys.
JSON:
{
identifier: 'itemId',
// you dont seem to have child references any but these are defaults
childrenAttrs: ['items', 'children'],
items: [
{
itemId: 1234,
name: 'Name'
}, {
...
}
]
}
then in your code, instead of using .xhr use .fetch in a store like so:
// 1.7+ syntax, pulling in dependencies.
// We want accordion and some other display widget like contentpane.
// Also we await domReady before calling our function
require(["dojo/data/ItemFileReadStore", "dijit/layout/AccordionContainer", "dijit/layout/ContentPane", "dojo/domReady!"], function(itemStore, accordion, contenpane) {
// this section is called when loading of itemstore dependencies are done
// create store
var store = new itemStore({
url:"json/"file_Name".json"
});
// create container
var acc = new accordion({style:"height: 300px"}, 'someDomNodeId');
// call XHR via .fetch and assign onComplete (opposed to 'load') callback
store.fetch({ onComplete: function(items) {
// items is an array of all the objects kept in jsonObject.items array
items.forEach(function(item) {
acc.addChild(new contentpane({
title: "Name for id: " + store.getValue(item, 'itemId'),
content: store.getValue(item, 'name')
}));
});
console.log(acc.getChildren()); // << would print out an array of contentpane widgets
});
});
This is howto :)
At any given time you could use the store and fetch some items, lets say you want to filter out some specific ones, call .query like so: store.fetch({query: { name: '*foo'/*all item.name ending with foo*/ }, onComplete: function(items) { /*cb func*/});
See
http://livedocs.dojotoolkit.org/dijit/layout/AccordionContainer#programmatic-example
and
http://livedocs.dojotoolkit.org/dojo/data/ItemFileReadStore