I have a nested array of objects like this:
var posts = [
{
_id:1234,
body:"text",
comments:[
{
_id:234,
body:"hello world", {
]
},
{
_id:434,
body:"hello world",
replies:[
{
_id:0e2345,
body:"hello",
{
]
}
]
}
]
I want to use normalizr to simplify array and use with redux. I have read the Normalizr documentation but it has few examples and I do not know what I am doing wrong.
I have tried the following code without success. The result I get is an array with undefined.
export function getPosts(state, action) {
const { payload } = action;
const { data} = payload;
const normalized = new schema.Entity("posts", {}, { idAttribute: "_id",});
const normalizedData = normalize(data, [normalized]);
return {
...state,
normalizedData,
};
}
I need something like this:
entities:{
posts:{
123:{
_id:123,
body:"hello world",
comments:{
234:{
_id:234,
body:"hello world",
replies:{
0e2345:{
_id:0e2345,
body:"oh no"
}
}
}
}
}
}
}
I've tried to reproduce this using a JSON object:
[{
"_id":1234,
"body":"text",
"comments":[
{
"_id":234,
"body":"hello world" }
]
},
{
"_id":434,
"body":"hello world",
"replies":[
{
"_id":0e2345,
"body":"hello"
}
]
}
]
And making the structure in the code:
import data from "./data.json";
import { normalize, schema } from "normalizr";
const _id = new schema.Entity('_id');
const body = new schema.Entity('body');
const normalized = new schema.Entity("posts", {
posts:{
_id:{
_id:_id,
body:body,
comments:{
_id:{
_id:_id,
body:body,
replies:{
_id:{
_id:_id,
body:body
}
}
}
}
}
}
});
const normalizedData = normalize(data, [normalized]);
console.log(normalizedData);
I get the next console output.
Related
I am having json object like below which will be dynamic,
let data_existing= [
{
"client":[
{
"name":"aaaa",
"filter":{
"name":"123456"
}
}
]
},
{
"server":[
{
"name":"qqqqq",
"filter":{
"name":"984567"
}
}
]
},
]
From the inputs i will get an object like below,
let data_new = {
"client":[
{
"name":"bbbbb",
"filter":{
"name":"456789"
}
}
]
}
I need to append this object into the existing "client" json object. Expected output will be like,
[
{
"client":[
{
"name":"aaaa",
"filter":{
"name":"123456"
}
},
{
"name":"bbbb",
"filter":{
"name":"456789"
}
}
]
},
{
"server":[
{
"name":"qqqqq",
"filter":{
"name":"984567"
}
}
]
}
]
And, if the "data_new" is not exists in the main objects, it should as new objects like below, for example,
let data_new = {
"server2":[
{
"name":"kkkkk",
"filter":{
"name":"111111"
}
}
]
}
output will be like,
[
{
"client":[
{
"name":"aaaa",
"filter":{
"name":"123456"
}
},
]
},
{
"server":[
{
"name":"qqqqq",
"filter":{
"name":"984567"
}
}
]
},
{
"server2":[
{
"name":"kkkkk",
"filter":{
"name":"11111"
}
}
]
}
]
I tried the below method, but it is not working as expected. Some help would be appreciated.
Tried like below and not worked as expected,
function addData(oldData, newData) {
let [key, value] = Object.entries(newData)[0]
return oldData.reduce((op, inp) => {
if (inp.hasOwnProperty(key)) {
console.log("111");
op[key] = inp[key].concat(newData[key]);
} else {
console.log(JSON.stringify(inp));
op = Object.assign(op, inp);
}
return op
}, {})
}
Your function seems to work when the key already belongs to data_existing (e.g.: "client").
But you have to handle the second use-case: when the key was not found in the objects of data_existing (e.g.: "server2").
This shall be performed after the reduce loop, adding the new item to data_existing if the key was not found.
Here is an example of how you could achieve that:
function addData(inputData, inputItem) {
const [newKey, newValue] = Object.entries(inputItem)[0];
let wasFound = false; // true iif the key was found in list
const res = inputData.reduce((accumulator, item) => {
const [key, value] = Object.entries(item)[0];
const keyMatch = key === newKey;
if (keyMatch) {
wasFound = true;
}
// concatenate the lists in case of key matching
const newItem = { [key]: keyMatch ? [...value, ...newValue] : value };
return [...accumulator, newItem];
}, []);
if (!wasFound) {
res.push(inputItem); // if key was not found, add item to the list
}
return res;
}
Hope it helps.
I just created a constructor function to create new Users for a JSON file.
The structure should be like:
{
"users": {
"userName1": {
"salary": [
"1234"
]
},
"userName2": {
"salary": [
"4321"
]
}
}
}
My code looks like this atm:
export const userDataControllerMixin = {
data() {
return {
userObj: {},
};
},
methods: {
NewUser(user, salary) {
this.user = user;
this.salary = salary;
user = {
salary,
};
},
// GETTING INPUT FROM USERS DIALOGBOX
getInput(inputName, inputSalary) {
const userName = document.querySelector(inputName).value;
const userSalary = document.querySelector(inputSalary).value;
const userData = new this.NewUser(userName, userSalary);
console.log(userData);
},
The structur i get is wrong, it looks like this:
NewUser {user: "asd ", salary: "123"}
When you use the word this, it means the current father, in your case NewUser
To get the variable the way you want, you need to do this:
NewUser(user, salary) {
this[user] = {
'salary':salary
};
},
In VueJS there is no need for querySelectors, since inputs are binded with v-model
Check out: https://v2.vuejs.org/v2/guide/forms.html
Because of that, we can reduce the app to one function, that reads the username and salary properties and adds them to the userObj.
I've made a working example here: https://codepen.io/bergur/pen/agZwQL?editors=1011
new Vue({
el: '#app',
data() {
return {
username: '',
salary: '',
userObj: {}
}
},
methods: {
newUser() {
this.userObj[this.username] = {
salary: [Number(this.salary)]
}
console.log(this.userObj)
}
}
})
My method works when I put values in new Farm () but not when I run it through postman in JSON which is also displayed below. I do not think I am parsing the JSON incorrectly. Any suggestions would be appreciated ?
export interface IDataFlowService {
handle(request: IDataFlowRequest): Promise<IDataFlowResponse>;
}
export class DataFlowService implements IDataFlowService {
current_env = process.env.NODE_ENV;
async handle(request: IDataFlowRequest): Promise<IDataFlowResponse> {
let response: IDataFlowResponse;
try {
switch (request.requestType) {
case "SaveFarms":
response = await this.SaveFarms(request);
break;
default:
winston.error(`Request failed for request type: ${request.requestType}`);
break;
}
return response;
} catch (e) {
winston.error(`handle method failed for request: ${request}`, e);
response = {
success: false,
error: {
'message': e.message,
}
};
return response;
}
}
private async SaveFarms(request: IDataFlowRequest): Promise<IDataFlowResponse> {
const response: IDataFlowResponse = {
success: true ,
farmIds: [],
names: []
};
for (const farm of request.farms) {
const newFarm: IFarmModel = new Farm();
Promise.all([newFarm.save()]);
response.farmIds.push(newFarm.farmId) ;
response.names.push(newFarm.name) ;
}
return response;
}
}
here is post: http://localhost:5000/api/dataflow
{
"requestType":"SaveFarms",
"farms": [
{
"name" : "Bronx"
}
]
}
as you can see I get a response but the names field is coming back null/empty:
{
"success": true,
"farmIds": [
"fH1WjllXR"
],
"names": [
null
]
}
You forget set name for newFarm object:
const newFarm: IFarmModel = new Farm();
newFarm.name = farm.name; // I think so
I have a JSON object stored in "aggRequest", this is my JSON
{
"state":{
"controllerStates":[
],
"rulesetStates":[
{
"rulesetContext":{
"dialog_stack":[
{
"dialog_node":"root"
}
],
"dialog_turn_counter":1,
"dialog_request_counter":1,
"_node_output_map":{
"node_1_1504607088493":[
0
]
},
"branch_exited":true,
"branch_exited_reason":"completed"
},
"convId":"XXXX",
"modelRef":"XXXX"
}
],
"selfCallCount":0,
"sysTurnCount":2,
"userTurnCount":2
},
"context":{
"conversationContext":{
"brand":"XXXX",
"channel":"XXX"
},
"userData":{
"tokens":[
]
}
},
"XXXXX":"2.0",
"XXXXX":"XXXXX",
"XXXXX":"XXXXX",
"XXXXX":"XXXXX",
"XXXXX":"XXXXX",
"turnStart":"2018-08-10T15:21:36.075Z",
"turnEnd":"2018-08-10T15:21:36.076Z"
}
I am trying to set the turnEnd to a new date using moment, the date is being created correctly, and I believe my function is correct:
const update = require('immutability-helper');
function updatePropertyValue() {
const momentDateChange = getRunDateAsString();
update(aggRequest, {
$set:
{
turnEnd: `${momentDateChange}`,
},
});
console.log(aggRequest)
}
When I view the addRequest console.log, the date has not been updated. Why would that be?
The whole idea of immutability is that you can not change the data. Instead you creating a new one. Like in the example below:
const update = require('immutability-helper');
const data = {
"turnEnd":"2018-08-10T15:21:36.076Z"
};
const newData = update(data, { $set: { turnEnd: 'voala!' } });
console.log(data); // it is immutable, right?
console.log(newData); // it is new version of the data
In order to solve the issue change your code to use updated (new version) of your data:
const updateAggRequest = update(aggRequest, { $set: { turnEnd: momentDateChange } });
My store looks like this,
{
name: "john",
foo: {},
arr: [
{
id:101,
desc:'comment'
},
{
id:101,
desc:'comment2'
}
]
}
My textarea looks like this
<textarea
id={arr.id} //"101"
name={`tesc:`}
value={this.props.store.desc}
onChange={this.props.onChng}
/>
My action is
export const onChng = (desc) => ({
type: Constants.SET_DESC,
payload: {
desc
}
});
My reducer
case Constants.SET_DESC:
return update(state, {
store: {
streams: {
desc: { $set: action.payload.desc }
}
}
});
It works only if arry is an object, I had to make changes to the stream to an array and I am confused how I can update to an array, also how does get the right value from the store.
The following example taken from the redux documentation might help you in the use case how to update items in an array. For more on this you can read on here http://redux.js.org/docs/recipes/StructuringReducers.html
state structure is something like this
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
and reducer code is like below
function updateObject(oldObject, newValues) {
// Encapsulate the idea of passing a new object as the first parameter
// to Object.assign to ensure we correctly copy data instead of mutating
return Object.assign({}, oldObject, newValues);
}
function updateItemInArray(array, itemId, updateItemCallback) {
const updatedItems = array.map(item => {
if(item.id !== itemId) {
// Since we only want to update one item, preserve all others as they are now
return item;
}
// Use the provided callback to create an updated item
const updatedItem = updateItemCallback(item);
return updatedItem;
});
return updatedItems;
}
function appReducer(state = initialState, action) {
switch(action.type) {
case 'EDIT_TODO' : {
const newTodos = updateItemInArray(state.todos, action.id, todo => {
return updateObject(todo, {text : action.text});
});
return updateObject(state, {todos : newTodos});
}
default : return state;
}
}
If you have to update an element in a array within your store you have to copy the array and clone the matching element to apply your changes.
So in the first step your action should contain either the already cloned (and changed) object or the id of the object and the properties to change.
Here is a rough example:
export class MyActions {
static readonly UPDATE_ITEM = 'My.Action.UPDATE_ITEM';
static updateItem(id: string, changedValues: any) {
return { type: MyActions.UPDATE_ITEM, payload: { id, changedValues } };
}
}
export const myReducer: Reducer<IAppState> = (state: IAppState = initialState, action: AnyAction): IAppState => {
switch (action.type) {
case MyActions.UPDATE_ITEM:
return { ...state, items: merge(state.items, action.payload) };
default:
return state;
}
}
const merge = (array, change) => {
// check if an item with the id already exists
const index = array.findIndex(item => item.id === change.id);
// copy the source array
array = [...array];
if(index >= 0) {
// clone and change the existing item
const existingItem = array[index];
array[index] = { ...existingItem, ...change.changedValues };
} else {
// add a new item to the array
array.push = { id: change.id, ...change.changedValues };
}
return array;
}
To update an array, I would use immutability helper and do something like this - to your reducer
let store = {"state" : {
"data": [{
"subset": [{
"id": 1
}, {
"id": 2
}]
}, {
"subset": [{
"id": 10
}, {
"id": 11
}, {
"id": 12
}]
}]
}}
case Constants.SET_DESC:
return update(store, {
"state" : {
"data": {
[action.indexToUpdate]: {
"subset": {
$set: action.payload.desc
}
}
}
}
})
});