Jerry's Blog

Recording what I learned everyday

View on GitHub


28 July 2019

Angular (43) -- Add Update and Delete Recipes

by Jerry Zhang

LeetCode Day 23: P168. Excel Sheet Column Title (Easy)

题目:

给一个整数,求在Excel sheet中对应的大字字母列号。

1 -> A
2 -> B
3 -> C
...
26 -> Z
27 -> AA
28 -> AB 

我的思路:

一个字母可以表示从1到26,两个字母可以是2626个,三个字母有2626*26个。所以其实是一个等比数列求和公式。 但是不知道有多少项,也就是不知道有多少位。所以一个一个减掉,看还剩下多少。但是如果这个整数非常大,接近整数的最大值,可能会有bug。 所以这种情况要尽可能考虑进去。

想了半个来小时,突然意识到其实这东西就是26进制。。。

后来发现跟26进制还是有一点区别,因为如果A代表0的话,那AA就没意义了。花了三四个小时,终于没有bug了。。。

我的代码:

public class E_168_ExcelSheetColumnTitle {
    public static String convertToTitle(int n) {
        String result = "";
        while (n / 26 > 0) {
            result = (char) (65 + (n - 1) % 26) + result;
            if (n % 26 == 0) {
                n = n - 26;
            }
            n = n / 26;
        }
        if (n != 0) {
            result = (char) (65 + (n - 1) % 26) + result;
        }
        return result;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 800; i++) {
            String s = convertToTitle(i + 1);
            System.out.println(s);
        }
    }
}

最优解:

class Solution {
    public String convertToTitle(int n) {
        StringBuilder sb = new StringBuilder();
        while (n > 0) {
            n--;
            int cur = n % 26;
            char c = (char)('A' + cur);
            sb.insert(0, c);
            n /= 26;
        }
        return sb.toString();
    }
}

跟我的思路差不多,但是他比我的简洁。我的错误在于把最后一种情况单独讨论了。如果合并到while循环里,就简单很多了。

把我的代码改造一下

public class E_168_ExcelSheetColumnTitle {
    public String convertToTitle(int n) {
        String result = "";
        while (n > 0) {
            n--;
            result = (char) ('A' + n % 26) + result;
            n = n / 26;
        }
        return result;
    }
}

Angular:

Create Actions:

The code was already copied in the previous blogs:

export const ADD_RECIPE = '[Recipe] Add Recipe';
export const UPDATE_RECIPE = '[Recipe] Update Recipe';
export const DELETE_RECIPE = '[Recipe] Delete Recipe';

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) {}
}

Reducer

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

Dispatch these actions:

When submitting a recipe, we no longer use a service, but use a store instead to dispatch a new Action.

In recipe edit component:

onSubmit() {
    if (this.editMode) {
      // this.recipeService.updateRecipe(this.id, this.recipeForm.value);
      this.store.dispatch(
        new RecipesActions.UpdateRecipe({
          index: this.id,
          newRecipe: this.recipeForm.value
        })
      );
    } else {
      // this.recipeService.addRecipe(this.recipeForm.value);
      this.store.dispatch(new RecipesActions.AddRecipe(this.recipeForm.value));
    }
    this.onCancel();
  }

In recipe detail component:

onDeleteRecipe() {
    // this.recipeService.deleteRecipe(this.id);
    this.store.dispatch(new RecipesActions.DeleteRecipe(this.id));
    this.router.navigate(['/recipes']);
}

In the recipe resolver service:

resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
    Observable<Recipe[]> | Promise<Recipe[]> | Recipe[] {
    return this.store.select('recipes').pipe(
      take(1),
      map(recipesState => {
        return recipesState.recipes;
      }),
      switchMap(recipes => {
        if (recipes.length === 0) {
          this.store.dispatch(new RecipesActions.FetchRecipes());
          return this.actions$.pipe(
            ofType(RecipesActions.SET_RECIPES),
            take(1)
          );
        } else {
          return of(recipes);
        }
      })
    );
  }

In recipe edit component, we need to unsubscribe.

private storeSub: Subscription;

if (this.editMode) {
      // const recipe = this.recipeService.getRecipe(this.id);
      this.storeSub = this.store
        .select('recipes')
        .pipe(
          map(recipeState => {
            return recipeState.recipes.find((recipe, index) => {
              return index === this.id;
            });
          })
        )
        .subscribe(recipe => {
          recipeName = recipe.name;
          recipeImagePath = recipe.imagePath;
          recipeDescription = recipe.description;
          if (recipe.ingredients) {
            for (const ingredient of recipe.ingredients) {
              recipeIngredients.push(
                new FormGroup({
                  'name': new FormControl(ingredient.name, Validators.required),
                  'amount': new FormControl(ingredient.amount, [
                    Validators.required,
                    Validators.pattern(/^[1-9]+[0-9]*$/)
                  ])
                })
              );
            }
          }
        });
    }

ngOnDestroy(): void {
    if (this.storeSub) {
      this.storeSub.unsubscribe();
    }
  }

tags: Angular