26 July 2019
Angular (41) -- NgRx for Recipes module
by Jerry Zhang
LeetCode Day 21: P160. Intersection of Two Linked Lists (Easy)
题目:
两个linked list 在某点交汇,找到这个交汇的节点。
我的思路:
能想到的只有hashSet,性能肯定很差,所以就不试了,直接看答案。
答案:
解法1:非常巧妙,但不太容易理解
基本思路是,两个list无论长短是否相同,它们长度的和一定是相同的。所以就把两个list分别一步一步推进,同步推进,每一步都检查指针 是否相同。任何一个list推进到结尾时,都把指针转到另一个list。这样第二轮推进时,它们的长度差就对调了。这就意味着如果有交汇点, 第二轮一定会交汇上的,不论长度是否相同。
public class E_160_IntersectionOfTwoLinkedLists {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) {
return null;
}
ListNode a = headA;
ListNode b = headB;
while (a != b) {
a = (a == null) ? headB : a.next;
b = (b == null) ? headA : b.next;
}
return a;
}
}
解法2:这个效率是最高的,也很容易理解。
分别计量两个list的长度。然后如果长度不一样,就把较长的那个,推进到跟短的那个相同的位置。然后两个list再同步推进,直到找到 交汇点为止。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode cur = headA;
int lenA = 0;
while(cur != null){
lenA ++;
cur = cur.next;
}
cur = headB;
int lenB = 0;
while(cur != null){
lenB ++;
cur = cur.next;
}
ListNode curA = headA;
ListNode curB = headB;
if(lenA > lenB) {
for(int i=0; i<lenA-lenB; i++){
curA = curA.next;
}
}
if(lenA < lenB) {
for(int i=0; i<lenB-lenA; i++){
curB = curB.next;
}
}
while(curA!=null){
if(curA == curB) return curA;
curA = curA.next;
curB = curB.next;
}
return null;
}
}
Angular: NgRx for Recipes Module
In the recipes folder, create three files, recipe.reducer.ts
, recipe.action.ts
, recipe.effects.ts
.
- Recipe Reducer:
import {Recipe} from '../recipe.model';
import * as RecipesActions from './recipe.actions';
export interface State {
recipes: Recipe[];
}
const initialState: State = {
recipes: []
};
export function recipeReducer(
state = initialState,
action: RecipesActions.RecipeActions
) {
switch (action.type) {
case RecipesActions.SET_RECIPES:
return {
...state,
recipes: [...action.payload]
};
case RecipesActions.ADD_RECIPE:
return {
...state,
recipes: [...state.recipes, action.payload]
};
case RecipesActions.UPDATE_RECIPE:
const updatedRecipe = {
...state.recipes[action.payload.index],
...action.payload.newRecipe
};
const updatedRecipes = [...state.recipes];
updatedRecipes[action.payload.index] = updatedRecipe;
return {
...state,
recipes: updatedRecipes
};
case RecipesActions.DELETE_RECIPE:
return {
...state,
recipes: state.recipes.filter((recipe, index) => {
return index !== action.payload;
})
};
default:
return state;
}
}
Recipe reducer is exporting a function which will receive a state and an action, and then return a new state.
- Recipe Actions:
import {Action} from '@ngrx/store';
import {Recipe} from '../recipe.model';
export const SET_RECIPES = '[Recipes] Set Recipes';
export const FETCH_RECIPES = '[Recipes] Fetch Recipes';
export const ADD_RECIPE = '[Recipe] Add Recipe';
export const UPDATE_RECIPE = '[Recipe] Update Recipe';
export const DELETE_RECIPE = '[Recipe] Delete Recipe';
export const STORE_RECIPES = '[Recipe] Store Recipes';
export class SetRecipes implements Action {
readonly type = SET_RECIPES;
constructor(public payload: Recipe[]) {}
}
export class FetchRecipes implements Action {
readonly type = FETCH_RECIPES;
}
export class AddRecipe implements Action {
readonly type = ADD_RECIPE;
constructor(public payload: Recipe) {}
}
export class UpdateRecipe implements Action {
readonly type = UPDATE_RECIPE;
constructor(public payload: {index: number; newRecipe: Recipe}) {}
}
export class DeleteRecipe implements Action {
readonly type = DELETE_RECIPE;
constructor(public payload: number) {}
}
export class StoreRecipes implements Action {
readonly type = STORE_RECIPES;
}
export type RecipeActions =
| SetRecipes
| FetchRecipes
| AddRecipe
| UpdateRecipe
| DeleteRecipe
| StoreRecipes;
- Update App Reducer
In the app.reducer.ts
, add a new state called recipes.
export interface AppState {
shoppingList: fromShoppingList.State;
auth: fromAuth.State;
recipes: fromRecipes.State;
}
export const appReducer: ActionReducerMap<AppState> = {
shoppingList: fromShoppingList.shoppingListReducer,
auth: fromAuth.authReducer,
recipes: fromRecipes.recipeReducer
};
- Use the action
First, in the place we want to use this action, inject the store in the constructor.
import * as fromApp from '../../store/app.reducer';
constructor(
private actions$: Actions,
private http: HttpClient,
private store: Store<fromApp.AppState>
) {}
Second, dispatch the action. Once we get the recipes from the server, use NgRx action to set this recipe in the store.
import * as RecipesActions from '../recipes/store/recipe.actions';
...
this.store.dispatch(new RecipesActions.SetRecipes(recipes));
...
Third, subscribe the action/data. Also need to inject the store in the constructor. For example, in the recipe-list component:
import * as fromApp from '../../store/app.reducer';
constructor(private router: Router,
private route: ActivatedRoute,
private store: Store<fromApp.AppState>) { }
ngOnInit() {
this.recipeSubscription = this.store
.select('recipes')
.pipe(map(recipesState => recipesState.recipes))
.subscribe((recipes: Recipe[]) => {
this.recipes = recipes;
});
}
Because we only need the recipes array, but from the store, we get the recipesState. So we use the pipe and map to transfer them into recipes.
tags: Angular