I have a feeling this is a total noob question but I wasn't able to find any information so here goes.
I need to "translate" php data model objects to JavaScript data model objects for a node.js project. I am pretty new to node.js and I simply don't understand the syntax of the data model objects.
Here's the barebones template that was provided (user.js):
'use strict';
module.exports = function UserModel() {
return {
name: 'username'
};
};
Here's part of the php model I am working off of:
class Model_User extends Base_Model {
'name' => array(
'first' => 'first_name',
'last' => 'last_name',
),
'friends' => array(
'model' => 'User',
),
}
I have written ActionScript data model objects in the past and I thought JS would be mostly identical but in AS the data properties are declared as separate vars. Looking at the barebones template above, that doesn't seem to be the case for node.js JS data model objects.
What about:
var model = {
'name': [
{ 'first': 'first_name' },
{ 'last': 'last_name' }
],
'friends': [
{ 'model': 'User' }
]
};
This is basically creating a model object. It contains two key -> value pairs name & friends, which both contain arrays of objects.
Read this for more info on javascript objects: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects
Related
I wanting to replicate this array structure in Javascript, but I cannot figure out how to do it, in my php I have the following,
$myArray = array();
$myArray['location1'] = array('name' => 'Texas', 'person' => 'Jim');
$myArray['location2'] = array('name' => 'California', 'person' => 'Jeff');
This would leave me a structure that looks like,
myArray = array(
'location1' = array('name' => 'Texas', 'person' => 'Jim')
'location2' = array('name' => 'California', 'person' => 'Jeff')
)
What I wanting in javascript is an array that holds other arrays and the sub arrays are keyed is this possible?
As stated by Steven, Javascript Arrays are not associative. However, you can parse an Associative PHP Array as a JSON Object which will led to the same structure when passing from or to Client Side.
P.E In PHP you'd have
$myArray = [
"tag"=>"property",
"properties"=>[
"color"=>"blue",
"name"=>"A Name"
]
];
Then, you return this array as JSON
return json_encode($myArray);
And when you receive it on Javascript you do a JSON Parse with the response
function getMyObject()
{
// Code for retrieving the JSON From Server
//End of Code for JSON Retrieval
const myObject = JSON.parse(response);
}
This will led to an structure such as this in Javascript
console.log(myObject)
>>>>
{
tag:"property",
properties:{
color:"blue",
name:"A Name"
}
}
Which you can access with the object.key notation or object[key] in Javascript.
Most of frameworks do this in a transparent way. For example, if you're using Laravel when you return the Array as a response in an API route, it will automatically parse it as a JSON. And then, in a framework such as Angular, the HttpClient will automatically parse the JSON response as a JavaScript object.
Then, in the opposite direction, if you send a object to an Endpoint. It will automatically be converted to a JSON body in the request and parsed by the backend framework.
I am receiving a json response from an API call. I need to store its keys, and create an array of an object. I am intending to this array of an object is created dynamically no matter the keys of the response.
I've already got the keys like this:
const json_getAllKeys = data => {
const keys = data.reduce((keys, obj) => (
keys.concat(Object.keys(obj).filter(key => (
keys.indexOf(key) === -1))
)
), [])
return keys
}
That returned an array (using a sample json):
['name','username', 'email']
But I am trying to use that array to create an array of object that looks like this one
[
{
name: "name",
username: "username",
email: "Email",
}
];
I've been trying mapping the array, but got multiple objects because of the loop, and I need a single one to make it work.
keys.map(i=>({i:i}))
[
{ i: 'id' },
{ i: 'name' },
{ i: 'username' },
{ i: 'email' }
]
Any hint would be useful!
Thanks in advance :D
What you're looking for is Object.fromEntries, which is ECMA2019, I believe, so available in Node >=14 and will be provided as a polyfill if you employ babel.
I can't quite discern what your reduce should produce, but given the sample input, I would write
const input = ['name','username', 'email'];
const result = Object.fromEntries(input.map(name => ([name, name])));
// result == { name: 'name', username: 'username', email: 'email' }
You're definitely on the right track. One thing to remember is the map function will return the SAME number of output as input. So in your example, an array of 3 returns an array of 3 items.
For this reason, map alone is not going to give you what you want. You may be able to map => reduce it. However, here is a way using forEach instead. This isn't a strictly functional programming style solution, but is pretty straight forward and depending on use case, probably good enough.
let keys = ['name','username', 'email'] //you have this array
const obj = {}; // empty object to hold result
keys.forEach(i => {
obj[i] = i; // set the object as you want
})
console.log(obj); // log out the mutated object
// { name: 'name', username: 'username', email: 'email' }
Working on a React, Redux + Typescript project, I am trying to add Immutable JS to the stack.
I started with working on a large nested object that could really use being safer as an immutable data structure.
import { Record, fromJS } from "immutable";
const obj = {
name: "werwr",
overview: {
seasons: {
2017: [{ period: 1, rates: 2 }]
}
}
};
// -- Using fromJS
const objJS = fromJS(obj);
const nObj = objJS.getIn(["overview", "seasons", "2017"]);
console.log(nObj); // I get an immutable list cool!
// -- Using Record, infer the type
const objRecord = Record(obj)();
const nRec = objRecord.getIn(["overview", "seasons", "2017"]);
console.log(nRec); // but I get a JS array
// -- Using both
const makeRec = Record(objJS);
const bothRecord = makeRec({ name: "name" });
console.log(bothRecord); // fails
Runnable code in codesandbox: https://codesandbox.io/s/naughty-panini-9bpgn?file=/src/index.ts
using fromJS. The conversion works well and deep but I lose all
type information.
using a Record. It keeps track of the type but nested arrays are
still mutable.
passing the converted object into a Record and manually add the type but I ran into an error: Cannot read property 'get' of
undefined
Whats the proper way to convert such an object to a fully immutable data structure while not loosing the type? Thanks!
You can use classes to construct deep structures.
interface IRole {
name: string;
related: IRole[];
}
const roleRecord = Record({
name: '',
related: List<Role>(),
});
class Role extends roleRecord {
name: string;
related: List<Role>;
constructor(config: IRole) {
super(Object.assign({}, config, {
related: config.related && List(config.related.map(r => new Role(r))),
}));
}
}
const myRole = new Role({
name: 'President',
related: [
{name: 'VP',
related:[
{name: 'AVP',
related: []}
]}
]});
With this type of structure, myRole will be all nested Role classes.
NOTE: I will add a bit of caution, we have been using this structure in a production application for almost 4 years now (angular, typescript, redux), and I added the immutablejs for safety from mutated actions and stores. If I had to do it over, the strict immutable store and actions that comes with NGRX would be my choice. Immutablejs is great at what it does, but the complexity it adds to the app is a trade off (Especially for onboarding new/greener coders).
Record is a factory for Record-Factories. As such, the argument should be an object template (aka default values), not actual data! (see docs).
const MyRecord = Record({
name: "werwr",
overview: null
});
const instance = MyRecord(somedata);
As you already noticed, the Record factory will not transform data to immutable. If you want to do that, you have to either do it manually with Maps and Lists, fromJS or the constructor of records.
The last approach is a bit weird, because then your record factory suddendly becomes a class:
const SeasonRecord = Record({
period: null, rates: null
})
class MyRecord extends Record({
name: "default_name",
seasons: Map()
}, 'MyRecord') {
constructor(values = {}, name) {
if(values.seasons) {
// straight forward Map of seasons:
// values = fromJS(values);
// Map of sub-records
values.seasons = Object.entries(values.seasons).reduce(
(acc, [year, season]) => {
acc[year] = SeasonRecord(season);
return acc;
}, {});
values.seasons = Map(values.seasons);
}
super(values, name);
}
}
const x = new MyRecord({
seasons: {
2017: { period: 1, rates: 2 }
}
})
console.log('period of 2017', x.seasons.get('2017').period)
I strongly suggest to not use unecessarily nest objects (record -> overview -> season) as it makes everything more complicated (and if you use large amounts of records, it might impact performance).
My general recommendation for Records is to keep them as flat as possible. The shown nesting of records allows to use the property access syntax instead of get, but is too tendious most of the time. Simply doing fromJS() for the values of a record and then use getIn is easier.
I am relatively new to Angular (more experienced with older version of Angular). I have two APIs, one is "/vehicles" and another is "/vehicle/{id}".
I get the "/vehicles" data and I loop through, match the IDs and then do another API call to "/vehicle/{id}" to get additional data for that particular vehicle and then create a new data object.
This all works but I get a console log error of Cannot read property 'id' of undefined so I'm assuming the template is looking for this data before it's finished getting the data (because the page loads as it should but can't get rid of this error).
This is the part of HTML issue I get - <h2>{{ car.id | uppercase }} {{ car.modelYear | uppercase }}</h2>
cars: Cars[] = [];
this.restApi.getCars().subscribe((cars: any = []) => {
cars.forEach((car: any = {}, i: string) => {
this.restApi.getCar(car.id).subscribe((c: any = {}) => {
if (car.id === c.id) {
this.cars[i] = {...c, ...car};
}
});
});
});
Here are the console logs of data
cars = [
{id: "xe", modelYear: "k17", url: "/api/vehicle/xe", media: Array(1)}
{id: "xf", modelYear: "k17", url: "/api/vehicle/xf", media: Array(1)}
{id: "xj", modelYear: "k16", url: "/api/vehicle/xj", media: Array(1)}
]
car = {
description: "Premium luxury saloon, spacious and beautiful yet powerfully agile."
id: "xj"
meta: {passengers: 5, drivetrain: Array(2), bodystyles: Array(2), emissions: {…}}
price: "£50,000"
}
My Rest Api service
getCars() {
return this.http.get(`${this.apiURL}/vehicles/`)
.pipe(
retry(1),
catchError(this.handleError)
);
}
getCar(id: string) {
return this.http.get(`${this.apiURL}/vehicle/${id}`)
.pipe(
retry(1),
catchError(this.handleError)
);
}
Thanks in advance
You should be careful when chaining observable subscribe. A better approach is to use rxjs operator like mergeMap to combine multiple http cals
this.restApi.getCars().pipe(
mergeMap((cars) => {
return cars.map( car => this.restApi.getCar(car.id).pipe(
map(carDetail => Object.assign(car, carDetail))
));
})
).subscribe((cars: any = []) => {
this.cars = cars
});
You are right here in assuming, that angular is attempting to access the data in cars array, before the service call is getting completed, to render the template. As the service is yet to return a response, angular gets undefined for all the child elements of car array.
One way to get rid of this problem would be to loop over the cars array instead of accessing the child element by specifying the index number(which I'm unsure if you're doing or not) as below -
<div *ngFor=" car in cars">
<h1>{{car.id}}</h1>
</div>
This will prevent angular from accessing child elements of cars array in case it is empty.
Another way to get rid of it would be to use a conditional render method. Simply add an if loop for the block rendering the cars data to render only if length of cars array is greater than zero. For instance,
<div ngIf="cars.length > 0">
<h1>{{cars[0].id}}</h1>
</div>
Although, this is something, I'm saying without having a proper look at your template file. Do let me know if this helps or if there's anything else I can help with
I'm querying a database in Javascript where I get back a Map object. The problem is that the key of some entry in the map is an object, an EnumValue to be precise.
I can't seem to find a way to directly retrieve this kind of entries. The only that comes to my mind is to iterate on every single key of the map and check if it's an object.
So, if I console.log the output I get from the query, it looks like this:
Map {
EnumValue { typeName: 'T', elementName: 'label' } => 'user',
'gender' => [ 'f' ],
'identityid' => [ '2349fd9f' ],
'name' => [ 'Erika' ],
'email' => [ 'test1#test.com' ],
EnumValue { typeName: 'T', elementName: 'id' } => 4136,
'lastname' => [ 'Delgato' ]
}
I've naively already tried to get the entry using something like this:
const enumId = { typeName: 'T', elementName: 'label' };
map.get(enumId)
But of course it returns undefined.
Any ideas?
So, for saving the time to anyone else that incours in this problem I'll write the solution here.
This problem is specific to values retrieval in a Janusgraph database, using the gremlin javascript client (version 3.4.2 used here).
When appending valueMap(true) to a traversal, the result is the one in the question I posted.
Going through the gremlin library, I found out that inside the object t from gremlin.process.t this EnumValues can be found:
id
key
label
value
Then using the id or label it is possible to directly retrieve the values with those nasty keys.
An example:
// Let's say we already have the Map from the question stored in a map variable
// To get the id of the result you can do the following
const t = gremlin.process.t;
const id = map.get(t.id);
console.log(id) // 4136
Map in javascript work by reference. It does not make a deep comparison, only check if the pointer is the same.
This will not work:
var x = new Map()
x.set({},'lala')
x.get({}) // undefined
But this will work:
var x = new Map()
var player = {name: 'ben'}
x.set(player, 50)
x.get(player) // 50