Jerry's Blog

Recording what I learned everyday

View on GitHub


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.

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.

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;

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
};

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