GDCNagpur Articles / Blogs / Perficient https://blogs.perficient.com/tag/gdcnagpur/ Expert Digital Insights Tue, 31 Dec 2024 02:31:48 +0000 en-US hourly 1 https://blogs.perficient.com/files/favicon-194x194-1-150x150.png GDCNagpur Articles / Blogs / Perficient https://blogs.perficient.com/tag/gdcnagpur/ 32 32 30508587 From Code to Cloud: AWS Lambda CI/CD with GitHub Actions https://blogs.perficient.com/2024/12/30/from-code-to-cloud-aws-lambda-ci-cd-with-github-actions/ https://blogs.perficient.com/2024/12/30/from-code-to-cloud-aws-lambda-ci-cd-with-github-actions/#respond Tue, 31 Dec 2024 02:31:48 +0000 https://blogs.perficient.com/?p=374755

Introduction:

Integrating GitHub Actions for Continuous Integration and Continuous Deployment (CI/CD) in AWS Lambda deployments is a modern approach to automating the software development lifecycle. GitHub Actions provides a platform for automating workflows directly from your GitHub repository, making it a powerful tool for managing AWS Lambda functions.

Understanding GitHub Actions CI/CD Using Lambda

Integrating GitHub Actions for CI/CD with AWS Lambda streamlines the deployment process, enhances code quality, and reduces the time from development to production. By automating the testing and deployment of Lambda functions, teams can focus on building features and improving the application rather than managing infrastructure and deployment logistics. This integration is essential to modern DevOps practices, promoting agility and efficiency in software development.

Prerequisites:

  • GitHub Account and Repository:
  • AWS Account:
  • AWS IAM Credentials:

DEMO:

First, we will create a folder structure like below & open it in Visual Studio.

Image 1

After this, open AWS Lambda and create a function using Python with the default settings. Once created, we will see the default Python script. Ensure that the file name in AWS Lambda matches the one we created under the src folder.

Image 2

Now, we will create a GitHub repository with the same name as our folder, LearnLambdaCICD. Once created, it will prompt us to configure the repository. We will follow the steps mentioned in the GitHub Repository section to initialize and sync the repository.

Image 3

Next, create a folder named .github/workflows under the main folder. Inside the workflows folder, create a file named deploy_cicd.yaml with the following script.

Image 4

As per this YAML, we need to set up the AWS_DEFAULT_REGION according to the region we are using. In our case, we are using ap-south-1. We will also need the ARN number from the AWS Lambda page, and we will use that same value in our YAML file.

We then need to configure the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. To do this, navigate to the AWS IAM role and create a new access key.

Once created, we will use the same access key and secret access key in our YAML file. Next, we will map these access keys in our GitHub repository by navigating to Settings > Secrets and variables > Actions and configuring the keys.

Updates:

We will update the default code in the lambda_function.py file in Visual Studio. This way, once the pipeline builds successfully, we can see the changes in AWS Lambda as well. This modified the file as shown below:

Image 5

Our next step will be to push the code to the Git repository using the following commands:

  • Git add .
  • Git commit -m “Last commit”
  • Git push

Once the push is successful, navigate to GitHub Actions from your repository. You will see the pipeline deploying and eventually completing, as shown below. We can further examine the deployment process by expanding the deploy section. This will allow us to observe the steps that occurred during the deployment.

Image 6

Now, when we navigate to AWS Lambda to check the code, we can see that the changes we deployed have been applied.

Image 7

We can also see the directory changes in the left pane of AWS Lambda.

Conclusion:

As we can see, integrating GitHub Actions for CI/CD with AWS Lambda automates and streamlines the deployment process, allowing developers to focus on building features rather than managing deployments. This integration enhances efficiency and reliability, ensuring rapid and consistent updates to serverless applications. By leveraging GitHub’s powerful workflows and AWS Lambda’s scalability, teams can effectively implement modern DevOps practices, resulting in faster and more agile software delivery.

]]>
https://blogs.perficient.com/2024/12/30/from-code-to-cloud-aws-lambda-ci-cd-with-github-actions/feed/ 0 374755
The Ultimate Guide for Cutting AWS Costs https://blogs.perficient.com/2024/07/30/the-ultimate-guide-for-cutting-aws-costs/ https://blogs.perficient.com/2024/07/30/the-ultimate-guide-for-cutting-aws-costs/#respond Wed, 31 Jul 2024 01:35:30 +0000 https://blogs.perficient.com/?p=366360

AWS cloud solution is becoming a requirement of the fast-evolving infrastructure needed in today’s IT business. All clients wish to move to cloud because it has higher availability and durability. The current consumers on cloud are always concerned with the ways that they can cut costs by a huge on Amazon web service monthly and or yearly billing cycle.
In this article we will examine such AWS resources & how, by using them, you can minimize your billing period.

1) AWS Cost allocation Tags

Using AWS tags we can track the resources that relate to each other. We can enable detailed cost report. The allocated tags show up in billing in column wise structure.

AWS generated cost allocation tagsUser tags
AWS tags will be automatically applied to resources we created if we have not tagged themThese tags are defined by the user. They start with prefix “user:”
It will start with prefix “aws:” e.g. (aws: createdBy).
They are not applied to the resources created before the activation

These cost allocation tags only show up in the billing console segment. Generally, it may take up to 24 hours for the tags to appear in the report.

To Activate Cost Allocation Tags:

Go to the AWS Billing and Cost Management console.

Select “Cost Allocation Tags” under “Billing preferences.”

Activate the tags you want to use for cost allocation by checking them.

Aaa

2) Trusted Advisor

This is a high level in AWS service assessment. This aids in the assessment of or proposing options such as cost management, robustness, reliability, scalability, quality of service, quality of operations. It is the same for all customers of AWS It offers core checks and basic suggestions.

To benefit from the full-on usage of this service you may have to be on a commercial and enterprise plan. We can make automatic reports and alerts for specific checks to stay informed about your AWS environment’s health and compliance with best practices.

From AWS management console, we can find Trusted advisor under support section.

Bbb

In trusted advisors, a service limit is used to monitor the recommendation. We can create manual cases from AWS support centre to increase limits or by using AWS service quotas service.

3) AWS Service Quotas

AWS Service Quotas, or limits, define the maximum number of resources or operations allowed within an AWS account. These quotas help ensure the stability and security of the AWS environment while providing predictable performance. AWS automatically sets these quotas, but many can be adjusted upon request.

We can setup CloudWatch Monitor usage against quotas and create alarms to alert you when you are nearing a quota limit.

Managing Service Quotas

  • AWS Management Console: Use the Service Quotas dashboard to view and manage your service quotas.

Ccc

  • AWS CLI: Use commands like aws service-quotas list-service-quotas to list quotas.
  • AWS SDKs: Use AWS SDKs to programmatically retrieve quota information.

 

Categories:

Account Quotas: Limits that apply to your entire AWS account.

Service-specific Quotas: Limits that apply to specific services like EC2, S3, RDS, etc.

 

Common AWS Service Quotas

EC2RDSS3
Running On-Demand Instances: varies depending on the type of instance;
for example, 20 are available for instances
with generic purposes.
DB Instances: Each account has 40 DB instances.Buckets: 1100 per account by default.
Spot Instances: There is a cap on how many spot instances you can execute.Storage: 100 TB of storage is available for all DB instances.Object Size: 5 TB or more per object
Elastic IP Addresses: 5 in each region.Snapshots: 100 manual snapshots per account.

4) AWS Saving Plans

Savings Plans promise a fixed level of usage (measured in $ / hour) for one or three years in exchange for a flexible pricing strategy that offers significant savings over On-Demand pricing.

Compute Savings Plans:EC2 Instance Savings Plans:
most flexible and cost-effectiveOffer maximum savings of up to 72%.
Regardless of region, instance family, operating system, or tenancy, apply to every EC2 instance.Specific to individual instance families in a chosen region
can also be used with AWS Lambda and Fargate.

Reserved Instances (RIs)

When compared to On-Demand pricing, Reserved Instances offer a substantial reduction of up to 75%. You can reserve capacity for your EC2 instances with them, but they do require a one- or three-year commitment.

Types of Reserved Instances:

Standard: Standard: Provide the largest discount possible; this is ideal for use in steady-state conditions.
Convertible: Offer savings while enabling changes to operating systems, tenancies, and instance kinds.

5) S3 – Intelligent Tiering

Amazon S3 Intelligent-Tiering is developed to automatically optimize storage costs as patterns of data access change. Without affecting performance or adding overhead, it transfers data between the frequent and seldom access tiers based on shifting access patterns.

Regardless of the access tier, there are no retrieval fees to retrieve your data. The cost of keeping an eye on things and transferring them across access tiers is covered by a little monthly monitoring and automation fee that is charged per object. Offers superior resilience and accessibility compared to alternative Amazon S3 storage categories.

Enabling S3 Intelligent-Tiering

Ddd

AWS Management Console: Navigate to the S3 bucket, select the objects, and choose “Change storage class” to move objects to S3 Intelligent-Tiering.

Alternatively, to move objects to Intelligent-Tiering, build up a lifecycle rule.

AWS CLI: To move things to Intelligent-Tiering, use the commands “aws s3 mv” or “aws s3 cp.”

6) AWS Budgets

Using AWS Budgets, a cost management tool, you can create personalized spending plans to monitor your AWS expenses, usage, and consumption. With its alerts, you may efficiently manage your AWS expenditure by being informed when you surpass or are expected to surpass your budget limitations.

Custom Budgets – Make expenses and utilization, Reserved Instances (RIs), Savings Plans, and Custom Budgets-based budgets. Establish budgets for several timeframes, such as monthly, quarterly, and annual.

Alerts and Notifications – When your budget is exceeded by actual or projected usage, get warnings via email or Amazon SNS. To receive warnings at different stages for the same budget, set up several thresholds.

Creating a Budget:

Open the AWS Budgets Dashboard.

Click on “Create a budget.” Follow the requirements and click on create Budget.

Eee

 

7) AWS Compute Optimizer

It helps in the optimization of your AWS resources, including Lambda functions, Auto Scaling groups, EBS volumes, and EC2 instances. It offers suggestions to boost productivity, cut expenses, and improve efficiency based on your usage behaviours.

EC2 Instances offers the best instance types based on how much memory, CPU, and network are used.

Auto Scaling Groups: suggests the ideal sizes and types of instances for groups.

EBS Volumes: makes recommendations for improving the types and settings of EBS volumes.

Lambda Functions: offers suggestions for maximizing concurrency and memory size.

For thorough cost management and monitoring, integrates easily with AWS services like AWS CloudWatch, AWS Budgets, and AWS Cost Explorer.

Enable AWS Compute Optimizer:

Go to the AWS Compute Optimizer Console.

Click “Get started” and follow the instructions to enable the service.

Fff

Example Use Case

EC2 Instance Optimization – To reduce expenses, find unused EC2 instances and reduce their size. To increase performance, find instances that are being overused and upgrade them.

Auto Scaling Group Optimization – To guarantee economical and effective scaling, optimize instance sizes and kinds within Auto Scaling groups.

Conclusion

We now know the seven most crucial things to do to reduce your AWS billing cycle. In the majority of situations, we can use CloudWatch to receive alerts when the threshold is reached. This will minimize needless billing for our management and maximize available resources.

]]>
https://blogs.perficient.com/2024/07/30/the-ultimate-guide-for-cutting-aws-costs/feed/ 0 366360
Integrating KeywordLogger with Test Listeners in Katalon Studio https://blogs.perficient.com/2024/06/10/integrating-keywordlogger-with-test-listeners-in-katalon-studio/ https://blogs.perficient.com/2024/06/10/integrating-keywordlogger-with-test-listeners-in-katalon-studio/#respond Mon, 10 Jun 2024 13:31:42 +0000 https://blogs.perficient.com/?p=363956

Effective logging is crucial in test automation. It helps testers track test execution, identify issues, and understand skipped steps. Katalon Studio’s KeywordLogger provides a versatile logging mechanism, which can be enhanced further by integrating with Test Listeners. This blog will guide you through the integration process, offering practical examples to illustrate the concepts.

 

What is KeywordLogger?

KeywordLogger is a logging utility in Katalon Studio that allows you to log different types of messages, such as informational messages, errors, and skipped steps. These logs provide a detailed account of the test execution flow, aiding in debugging and reporting.

Setting Up Test Listeners

To set up Test Listeners in Katalon Studio, you need to create a new Groovy script in the Test Listeners folder of your Katalon project. This script will define methods annotated with listener annotations like @BeforeTestCase, @AfterTestCase, @BeforeTestSuite, and @AfterTestSuite.

Creating a Test Listener

  1. Create the Test Listeners folder in your Katalon project.
  2. Create a new Groovy script. For instance, name it LoggingListener.

Integrating KeywordLogger with Test Listeners

Let’s create a comprehensive Test Listener that logs messages at different stages of the test lifecycle.

 

Step-by-Step Guide

  1. Import necessary classes:

import com.kms.katalon.core.logging.KeywordLogger

import com.kms.katalon.core.annotation.BeforeTestCase

import com.kms.katalon.core.annotation.AfterTestCase

import com.kms.katalon.core.annotation.BeforeTestSuite

import com.kms.katalon.core.annotation.AfterTestSuite

import com.kms.katalon.core.context.TestCaseContext

import com.kms.katalon.core.context.TestSuiteContext

 

  1. Initialize the KeywordLogger:
KeywordLogger logger = new KeywordLogger()

 

  1. Define the methods with appropriate annotations:
@BeforeTestCase

def beforeTestCase(TestCaseContext testCaseContext) {

    logger.logInfo("Starting test case: " + testCaseContext.getTestCaseId())

}

@AfterTestCase

def afterTestCase(TestCaseContext testCaseContext) {

    if (testCaseContext.getTestCaseStatus() == 'FAILED') {

        logger.logError("Test case failed: " + testCaseContext.getTestCaseId())

    } else {

        logger.logInfo("Completed test case: " + testCaseContext.getTestCaseId())

    }

}

@BeforeTestSuite

def beforeTestSuite(TestSuiteContext testSuiteContext) {

    logger.logInfo("Starting test suite: " + testSuiteContext.getTestSuiteId())

}

@AfterTestSuite

def afterTestSuite(TestSuiteContext testSuiteContext) {

    logger.logInfo("Completed test suite: " + testSuiteContext.getTestSuiteId())

}

 

Full Example

Here’s the complete code for a Test Listener that integrates KeywordLogger:

import com.kms.katalon.core.logging.KeywordLogger

import com.kms.katalon.core.annotation.BeforeTestCase

import com.kms.katalon.core.annotation.AfterTestCase

import com.kms.katalon.core.annotation.BeforeTestSuite

import com.kms.katalon.core.annotation.AfterTestSuite

import com.kms.katalon.core.context.TestCaseContext

import com.kms.katalon.core.context.TestSuiteContext

KeywordLogger logger = new KeywordLogger()

@BeforeTestCase

def beforeTestCase(TestCaseContext testCaseContext) {

    logger.logInfo("Starting test case: " + testCaseContext.getTestCaseId())

}

@AfterTestCase

def afterTestCase(TestCaseContext testCaseContext) {

    if (testCaseContext.getTestCaseStatus() == 'FAILED') {

        logger.logError("Test case failed: " + testCaseContext.getTestCaseId())

    } else {

        logger.logInfo("Completed test case: " + testCaseContext.getTestCaseId())

    }

}

@BeforeTestSuite

def beforeTestSuite(TestSuiteContext testSuiteContext) {

    logger.logInfo("Starting test suite: " + testSuiteContext.getTestSuiteId())

}

@AfterTestSuite

def afterTestSuite(TestSuiteContext testSuiteContext) {

    logger.logInfo("Completed test suite: " + testSuiteContext.getTestSuiteId())

}

 

Practical Examples

Let’s look at some practical examples to understand how this integration works in real-world scenarios.

Example 1: Logging Test Case Execution

Imagine you have a test case for verifying user login functionality. By using the integrated Test Listener, you automatically log the start and end of the test case execution.

Test Case Script:

import static com.kms.katalon.core.testcase.TestCaseFactory.findTestCase

import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI

WebUI.openBrowser('')

WebUI.navigateToUrl('https://example.com/login')

WebUI.setText(findTestObject('Page_Login/txt_Username'), 'username')

WebUI.setText(findTestObject('Page_Login/txt_Password'), 'password')

WebUI.click(findTestObject('Page_Login/btn_Login'))

WebUI.verifyElementPresent(findTestObject('Page_Home/lbl_LoggedIn'), 10)

WebUI.closeBrowser()

 

Logged Output:

INFO: Starting test case: Test Cases/Verify User Login

INFO: Completed test case: Test Cases/Verify User Login

 

If the login fails, the output will include an error log:

INFO: Starting test case: Test Cases/Verify User Login

ERROR: Test case failed: Test Cases/Verify User Login

 

Example 2: Logging Test Suite Execution

Consider a test suite that runs multiple test cases related to user registration. The integrated Test Listener logs the start and end of the test suite.

Test Suite Script:

  • Verify User Registration
  • Verify User Registration with Invalid Data
  • Verify User Registration without Mandatory Fields

 

Logged Output:

INFO: Starting test suite: Test Suites/User Registration Tests

INFO: Starting test case: Test Cases/Verify User Registration

INFO: Completed test case: Test Cases/Verify User Registration

INFO: Starting test case: Test Cases/Verify User Registration with Invalid Data

ERROR: Test case failed: Test Cases/Verify User Registration with Invalid Data

INFO: Starting test case: Test Cases/Verify User Registration without Mandatory Fields

INFO: Completed test case: Test Cases/Verify User Registration without Mandatory Fields

INFO: Completed test suite: Test Suites/User Registration Tests

 

Best Practices for Effective Logging

  • Combine Logs for Clarity

By combining logInfo, logError, and logSkipped messages, you create a detailed and clear log that covers all aspects of your test case execution. This practice ensures you have comprehensive logs for easy debugging and reporting.

  • Use Test Listeners for Consistency

Integrating KeywordLogger with Test Listeners ensures that logs are consistently recorded at key points in your test lifecycle without manual intervention. This approach helps maintain a standardized logging format across all test cases and suites.

  • Monitor and Review Logs Regularly

Regularly monitoring and reviewing your logs helps identify patterns, recurring issues, and opportunities for improving your test scripts and overall testing process. Consistent log review ensures that any issues are quickly identified and addressed, enhancing the reliability of your tests.

Conclusion

Integrating KeywordLogger with Test Listeners in Katalon Studio provides comprehensive and consistent logs, automatically capturing key events throughout your test lifecycle.

]]>
https://blogs.perficient.com/2024/06/10/integrating-keywordlogger-with-test-listeners-in-katalon-studio/feed/ 0 363956
useOptimistic: Powering Next.js https://blogs.perficient.com/2024/05/02/useoptimistic-powering-next-js/ https://blogs.perficient.com/2024/05/02/useoptimistic-powering-next-js/#respond Thu, 02 May 2024 14:25:21 +0000 https://blogs.perficient.com/?p=360284

In today’s blog, we will examine an experimental hook that helps us display optimized results when we display data we fetch from the server. What can we do to optimize such a use case? Is there a way to show updated data as it is being fetched from the server? We will explore precisely that with the new useOptimistic hook.

This hook from React gives us a copy of our data, which we pass to it, and a function like how useState works. This function is then utilized to manipulate the copied data we show in our application until the new data is fetched from the server or API call.

Implementing the “useOptimistic” Hook in Your Project

Like useFormStatus and useFormState in earlier blogs, this is an experimental hook by react-dom; therefore, it will not be available with the usual npm install package command. To accomplish this, run the following command in your terminal to install the experimental version of react and react-dom:

npm install react@experimental react-dom@experimental

After installing the experimental packages, your package.json should contain the dependencies listed below:

useOptimistic

After doing this, if you want to use typescript in your project, you should additionally create a file informing your project that you will be utilizing the experimental utilities:
useOptimistic

Following that, you should be able to import useFormState into your files and use it appropriately. If you use TypeScript, you may need to add “//@ts-ignore” above the import. TypeScript will not recognize this experimental import but will continue functioning as intended.

//@ts-ignore
import { useFormState } from "react-dom";

Developing a Simple User Interface

Let’s now create a simple react component showing an input and a button. You can use a form here as well; I have simply used the onClick property of the button to recreate the API service call scenario.

return (
    <div className="bg-dark text-white py-5">
      <div className="row mx-0">
        <h2 className="text-center">useOptimistic</h2>
        <div className="col-md-6 p-5">
          <input
            ref={inputRef}
            type="text"
            className="form-control my-3"
            placeholder="Enter item name"
          />
          <button
            disabled={isLoading}
            onClick={() => startTransition(() => onClick())}
            className="btn btn-warning form-control"
          >
            Add Item
          </button>
        </div>
      </div>
    </div>
  );

We now have a simple input and button component. The input component is passed with an inputRef created by using the useRef hook of react, and the button’s onClick function is wrapped in a startTransition function to simulate loading.

useOptimistic

The Utilities Listed in Form

Next, we look at the useful hooks we need to import into our app and some other useful utilities we will use to demonstrate our new hook.

import { useState, useOptimistic, useTransition, useRef } from "react";
import "bootstrap/dist/css/bootstrap.css";

function App() {
  const createItem = (item) => {
    return new Promise((resolve) => setTimeout(() => resolve(item), 2000));
  };
  const inputRef = useRef();
  const [isLoading, startTransition] = useTransition();

  const [itemList, setItemList] = useState([]);
  const [optimisticItems, setOptimisticItems] = useOptimistic(itemList);

As shown in the code above, we import all our hooks from React. As I mentioned earlier, we are using the “startTransition” function to simulate loading here. We are taking this function as the second value provided to us from the useTransition hook in React, the first being the loading state for the function we use startTransition with.

I have created a function here to replicate the API service call behavior of fetching data. It is the createItem function. It basically returns me a value with a 2-second delay. Other than this, we just have the inputRef, which we will use to get the data from input and useState to put the data in an array.

The last line of the code above is what we are looking for. We use the useOptimistic hook to duplicate our itemList, storing it in the variable optimisticItems. The second item we get from this hook is a function that updates the same copied state. We will use this method to initially update our copied state until the service call is complete and we have the correct data in our itemList variable. So, let’s examine the function where we will make this call.

const onClick = async () => {
   const inputValue = inputRef.current.value;
   if (inputRef.current == null || !inputValue) return;
   setOptimisticItems((list) => [
     ...list,
     {
       itemName: inputValue,
       key: crypto.randomUUID(),
       loading: true,
     },
   ]);
   const newItem = await createItem(inputValue);
   setItemList((list) => [
     ...list,
     {
       key: crypto.randomUUID(),
       itemName: newItem,
     },
   ]);
 };

setOptimisticItems Function

Let’s go through this code one by one. First, we take the value of input from inputRef and check to see if it is empty. If it is empty, we will simply return from the function. Next, we have the setOptimisticItems function, which is basically the main purpose of this blog.

Here, we are keeping it as simple as it could get. We get the previous state as the first parameter of this function, which would be the existing list of items, so we will spread that array and add our new value to its end. This now creates our data array and puts the latest data with it before the service call starts in the following line. We are also giving it a loading value which we will use to indicate this is not the complete data, more on that later.

In the following line, we are making the service call (or mocking a service call in this case), getting actual data from the service, and updating our state with that exact data. Therefore, until we perform the await operation, we have already displayed the desired data using the optimistic value. Let’s see how we can implement using the loading indicator I mentioned earlier.

<div className="col-md-6 p-5 ms-4">
  <div>Item's List:</div>
  <ul className="my-3">
    {optimisticItems.map(({ itemName, loading, key }) => (
      <li key={key} className={loading ? "opacity-50" : "opacity-100"}>
        {itemName}
        {loading ? "..." : null}
      </li>
    ))}
  </ul>
</div>

In the above code, we show that the value that is not loaded yet will be displayed with half opacity and will have an ellipsis at the end to indicate it is loading or waiting for the service call to be completed.

Output

Initial form state:

useOptimistic

The user enters a value in the input field:

Picture5

 

The Add button can be clicked to start a service call:
Picture9

The value appears less visible than standard text and has an ellipsis at the end to indicate the loading state. When the loading is complete or the API call successfully completes, it will appear in its normal state, as shown in the image below.

Picture7

This is basically how we can show the data before it is ultimately part of the database and indicate that proper data is loading using this hook. Another screenshot to show the same.

Picture10

One simple yet powerful way to optimize user interactions in Next.js apps is to include the ‘useOptimistic’ hook. Developers may guarantee improved user pleasure and better performance by implementing optimistic updates. ‘useOptimistic’ is a helpful utility for Next.js developers, offering better user experiences with less work thanks to its effect and simplicity.

]]>
https://blogs.perficient.com/2024/05/02/useoptimistic-powering-next-js/feed/ 0 360284
Advanced Array Methods in JavaScript: Part 3 https://blogs.perficient.com/2024/04/08/advanced-array-methods-in-javascript-part-3/ https://blogs.perficient.com/2024/04/08/advanced-array-methods-in-javascript-part-3/#respond Mon, 08 Apr 2024 10:36:53 +0000 https://blogs.perficient.com/?p=359525

Welcome back to the third part of our series on elevating your JavaScript skills through array methods. Having established a solid foundation with simple array methods, we’re now poised to tackle more advanced methods. In this blog, we will discover sophisticated array methods that offer greater flexibility and power in manipulating data. Prepare to unlock new ranges of programming prowess as we continue our deep dive into JavaScript’s array methods. If you haven’t yet, make sure to explore essential array methods in Part 1 and Part 2 of this series.

Advanced array methods encompass a diverse range of categories, each serving specific purposes in data manipulation. These include:

  • Array Find and Search Methods
  • Array Sort Methods and Tricks
  • Array Iteration Methods

These categories provide developers with powerful tools for locating elements, organizing data, and iterating through arrays efficiently, enhancing the capabilities of JavaScript programming.

Array Find and Search Methods

indexOf() and lastIndexOf()

These advanced array methods are like searchlights in the dark, helping you pinpoint the exact location of a specific element within an array. If the element is found, it reveals its index. indexOf() uncovers the first occurrence, while lastIndexOf() reveals the last. However, if the element is nowhere to be found, they report back -1, indicating that the search was unsuccessful.

Example:

const animals = ["Cheetah", "Lion", "Zebra", "Horse", "Cheetah", "Deer"];
const firstIndex = animals.indexOf("Cheetah");
console.log("firstIndex", firstIndex);
const lastIndex = animals.lastIndexOf("Cheetah");
console.log("lastIndex", lastIndex);

Output:
Advanced Array 1

includes()

It is used to determine whether a value is included in the system entry and returns true or false as appropriate.

Example:

const colors = ['red', 'green', 'blue'];
console.log("includes method:")
console.log(colors.includes('green'));
console.log(colors.includes('yellow'));

Output:
Advanced Array 2

find() and findIndex()

find()

This function helps find the first array element that meets a condition. If found, it returns the element; otherwise, it is undefined.

Example:

const movies = ["The Lion King", "Aladdin", "The Jungle Book", "Moana"];
const foundMovie = movies.find((movie) => movie === "Aladdin");
console.log("find method:\n", foundMovie);

Output:
Advanced Array 3

findIndex()

It returns the index of the first element in the array that satisfies the given testing function. If the function does not satisfy any element, it will return -1.

Example:

const movies = ["The Lion King", "Aladdin", "The Jungle Book", "Moana"];
const index = movies.findIndex((movie) => movie === "Moana");
console.log("findIndex method:\n", index);

Output:
Advanced Array 4

findLast()

This method fetches the last array element that meets the condition set by the provided testing function.

Example:

const numbers = [10, 20, 30, 40, 50];
const lastNumber = numbers.findLast(num => num > 20);
console.log("Output:",lastNumber);

Output:
Advanced Array 5

findLastIndex()

It retrieves the index of the last array element that fulfills the conditions set by the testing function.

Example:

const numbers = [10, 20, 30, 40, 50];
const lastIndex = numbers.findLastIndex(num => num > 20);
console.log("Last index of matched condition:",lastIndex); // Output: 4 (index of 50)

Output:
Advanced Array 6

Array Iteration Methods

forEach()

It’s like having a guide show you around a museum, stopping at each exhibit along the way. method executes a function for every element within an array.

Example:

const numbers = [1, 2, 3, 4, 5];
console.log("forEach method:")
numbers.forEach((num) => console.log(num * 2));

Output:
Advanced Array 7

flat() and flatMap()

flat()

Imagine you have a stack of nested trays, each containing some items. flat() is like taking out all the items from those trays and putting them into a single tray, simplifying the organization.

Example:

const nestedArray = [["Peter Pan", "Aladdin"], ["Mulan", "Maleficent"], ["Moana", "Tangled"]];
const flattenedArray = nestedArray.flat();
console.log("flat method:\n",flattenedArray);

Output:
Advanced Array 8

flatMap()

It’s like having a stack of notebooks, and you need to examine each page, write something on it, and then gather all those pages into a new notebook. flatMap() first maps over every element inside the array using a function you offer and then flattens the result into a new array, making it easier to deal with.

Example:

const numbers = [1, 2, 3];
const mappedAndFlattened = numbers.flatMap(num => [num * 2, num * 3]);
console.log("flatMap:\n",mappedAndFlattened);

Output:
Advanced Array 9

filter()

Think of it as a filter on a coffee machine that separates ground coffee from brewed coffee, ensuring that only pure water flows in. Filter() in JavaScript searches each element of an array to establish a condition specifically, storing only those elements that satisfy the condition and using those elements to create a new array.

Example:

const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter((num) => num % 2 === 0);
console.log("filter method:\n", evenNumbers);

Output:
Advanced Array 10

every() and some()

These methods are like gatekeepers, checking each element against a condition.

every():

The condition is checked if all elements are met.

Example:

const numbers = [2, 4, 6, 7, 8];
const allEven = numbers.every((num) => num % 2 === 0);
console.log("every method:\n", allEven);

output:
Advanced Array 11

some():

Checks if at least one element meets a condition.

Example:

const numbers = [2, 4, 6, 7, 8];
const anyEven = numbers.some((num) => num % 2 === 0);
console.log("some method:\n", anyEven);

Output:
Advanced Array 12

reduce():

It’s like having a calculator that provides all the numbers in a list for you. You provide a function that tells the calculator a way to combine every range with the running total.

Syntax:

array.reduce(function(total, currentValue, currentIndex, arr), initialValue)

Example:

const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((total, current) => total + current, 0);
console.log("reduce method:\n",sum);

Output:
Advanced Array 13

Example 2:

const items = [
  { name: "Shirt", price: 20 },
  { name: "Pants", price: 30 },
  { name: "Shoes", price: 50 },
];

const totalPrice = items.reduce((acc, item) => acc + item.price, 0);
console.log("reduce method:");
console.log("Total Price:", totalPrice);

Output:
Advanced Array 14

reduceRight()

The function reduces each value of the array (from right to left) against an accumulator to produce a single value.

Example:

const arr = [1, 2, 3, 4];
const sum = arr.reduceRight((accumulator, currentValue) => accumulator + currentValue);
console.log("Sum of all numbers:",sum); // Output: 10 (4 + 3 + 2 + 1)

Output:
reduceRight

Array Sort Methods

1. Array Alphabetic Sort

sort():

The elements of an array are sorted in place by the sort() method, and the sorted array is returned. It rearranges elements either in place or by creating a new sorted array.

Example:

const numbers = [36, 17, 84, 01, 65, 19, 22, 16];
const sortedNumbers = numbers.sort();
console.log("sort method:\n",sortedNumbers)

output:
sort

reverse()

reverse() The order of the elements in the array is reversed. It’s like looking at a mirror image of your array. Moves an array to its location and returns a reference to the same array; the first array element is now the last, and the last array element is the first.

Example:

const animals = ["Cheetah", "Lion", "Zebra", "Horse", "Deer"];
animals.reverse();
console.log("reverse method:");
console.log("Array in reverse order:", animals);

Output:
reverse

toSorted()

The array is returned with elements sorted in ascending order.

Example:

const numbers = [5, 2, 8, 1, 4];
const sortedNumbers = numbers.toSorted();
console.log("Sorted in ascending order",sortedNumbers);

Output:
toSorted

toReversed()

Returns the array with elements in reverse order.

Example:

const numbers = [1, 2, 3, 4, 5];
const reversedNumbers = numbers.toReversed();
console.log("Elements in reverse order:",reversedNumbers);

Output:
toReversed

2. Array Numeric Sort

Math.min()

The smallest number among the provided arguments is returned.

Example:

const minNumber = Math.min(22,15,34); 
console.log("Smallest number in the series:",minNumber);

Output:
Math.min

Math.max()

The function determines the largest number among the arguments provided.

Example:

const maxNumber = Math.max(10, 45, 20); 
console.log("Largest number in the series:",maxNumber);

Output:
Math.max

Conclusion

Our exploration of JavaScript array methods has led us from fundamental operations to more advanced techniques. These tools empower developers to manipulate data efficiently and think critically about problem-solving in JavaScript. By mastering these methods, you’ll enhance your coding skills and uncover deeper layers of JavaScript’s potential. Keep practicing and experimenting to unlock even greater possibilities in your coding journey. For those who started here, consider revisiting Essential Methods Part 1 and Part 2 to ensure a comprehensive understanding of array basics.

]]>
https://blogs.perficient.com/2024/04/08/advanced-array-methods-in-javascript-part-3/feed/ 0 359525
Understanding the Basics of TOGAF: A Beginner’s Guide https://blogs.perficient.com/2024/03/29/understanding-the-basics-of-togaf-a-beginners-guide/ https://blogs.perficient.com/2024/03/29/understanding-the-basics-of-togaf-a-beginners-guide/#respond Fri, 29 Mar 2024 05:14:33 +0000 https://blogs.perficient.com/?p=360514

Introduction

TOGAF, which stands for The Open Group Architecture Framework, is a widely recognized enterprise architecture framework used by leading businesses globally. This blog aims to provide a comprehensive beginner’s guide to understanding the basics of TOGAF, its significance, and how it can benefit organizations.

TOGAF is an enterprise architecture standard that offers a high-level framework for managing enterprise software development. Initially introduced by The Open Group in 1995, TOGAF has become a standard utilized by over 80% of Global 50 companies and 60% of Fortune 500 companies. It ensures architecture standards, consistent methodologies, and effective communication among enterprise architecture professionals.

Evolution of Enterprise Architecture

Enterprise Architecture, a subset of computer architecture, emerged in the mid-1960s and has evolved significantly over the years. Today, businesses of all sizes and scales rely on enterprise architecture frameworks like TOGAF to plan and design IT architecture, enhancing business efficiency and alignment with strategic goals.

Key Elements of TOGAF

  1. Architecture Development Method (ADM): The heart of TOGAF lies in the Architecture Project Management Method, which guides professionals through a cycle of enterprise architecture development. It outlines key steps for practitioners to follow, ensuring a clear and versatile methodology for architecture development.
  2. Spectrum of Change: TOGAF aims to transform enterprises from a baseline architecture to a target architecture, managing the spectrum of change effectively. It focuses on three primary architecture domains: business, information systems, and IT infrastructure, with a goal of standardization and integration.
  3. 4-Step Process: TOGAF consists of a 4-step process that includes establishing a vision, developing architecture, migration planning, and implementation governance. These steps provide a structured approach to architecture development and transformation.
  4. 8 Steps of Architecture Development: TOGAF’s architecture development is structured in 8 steps, including defining architecture vision, business architecture, information system architectures, technology architecture, opportunities and solutions, migration planning, implementation governance, and architecture change management.

Benefits of TOGAF

TOGAF offers several advantages to organizations and practitioners, including:

  • Providing a common language for architects to follow.
  • Enabling compatibility with other frameworks.
  • Offering flexibility and adaptability to suit different organizational needs.
  • Outlining related disciplines like business planning, risk management, and project management.
  • Facilitating a systematic and structured approach to enterprise architecture development.

 

Now let’s discuss the key components of TOGAF:

The key components of TOGAF include:

  1. Preliminary Phase: This initial stage involves laying the groundwork for the architecture development cycle. It includes establishing the architecture capability within the organization, understanding the business environment, and defining the scope of the architecture initiative.
  2. Architecture Vision: Creating a high-level view of the end architecture is crucial to aligning with business goals and strategic direction. This component sets the overarching goals and objectives that the architecture development process aims to achieve.
  3. Business Architecture: Focusing on the organization’s business strategy, goals, processes, and stakeholders is essential for ensuring that the architecture aligns with the business objectives. It involves understanding the current state of the business and defining how it should evolve.
  4. Information Systems Architecture: Addressing data assets, data modeling, storage, and management within the organization is vital for ensuring that information flows effectively and securely across the enterprise. This component focuses on structuring and managing data assets to support business operations.
  5. Technology Architecture: Managing the technology infrastructure, including hardware, networks, and software platforms, is key to supporting business operations and achieving strategic objectives. This component ensures that the technology landscape aligns with the overall architecture vision.
  6. Opportunities & Solutions: Identifying change parameters, major phases, and top-level projects for transitioning from the current to the target environment is crucial for driving transformation initiatives. This component focuses on analyzing opportunities for improvement and defining solutions to address business needs.
  7. Migration Planning: Prioritizing projects within the Technology Architecture and estimating costs for migrations is essential for effective implementation. This component involves planning the sequence of changes, allocating resources, and managing the transition to the target architecture.
  8. Implementation Governance: Assigning implementation organizations for each project and ensuring effective governance throughout the implementation process is critical for successful execution. This component involves establishing oversight mechanisms, defining roles and responsibilities, and monitoring progress.
  9. Architecture Change Management: Managing changes to the architecture over time and adapting to evolving technology or business environments is essential for maintaining alignment with business goals. This component focuses on ensuring that the architecture remains relevant and effective in a dynamic business landscape.

 

Conclusion:

TOGAF serves as a roadmap for organizations to navigate complex enterprise architecture challenges, ensuring alignment with business goals, effective communication, and successful transformation initiatives. By understanding the basics of TOGAF, businesses can leverage this framework to enhance their architecture practices and drive organizational success.

]]>
https://blogs.perficient.com/2024/03/29/understanding-the-basics-of-togaf-a-beginners-guide/feed/ 0 360514
Benefits of Implementing TOGAF in Your Organization https://blogs.perficient.com/2024/03/29/benefits-of-implementing-togaf-in-your-organization/ https://blogs.perficient.com/2024/03/29/benefits-of-implementing-togaf-in-your-organization/#respond Fri, 29 Mar 2024 05:14:28 +0000 https://blogs.perficient.com/?p=360730

Introduction:

The Open Group Architecture Framework (TOGAF) is a widely recognized and comprehensive method for developing and managing enterprise architectures. Implementing TOGAF in your organization can bring many benefits, from better alignment of IT with business goals to improved architecture development. This blog explores the benefits of implementing TOGAF and how it can positively impact your organization.

Advantages of adopting TOGAF

  • Enhanced Alignment with Business Objectives

TOGAF provides a structured approach to aligning IT strategies with overall business objectives. By following the TOGAF methodology, organizations can ensure that their technology initiatives directly support and drive business goals. This alignment improves efficiency, reduces redundancies and provides better visibility into how IT investments contribute to overall business success.

  • Improved Decision-Making and Governance

One of the most important benefits of implementing TOGAF is the improved decision-making processes and management structures it enables. TOGAF provides a systematic way to analyze, design and deploy architectures, providing a strong foundation for making informed decisions about technology investments. This structured approach also improves governance by defining clear roles, responsibilities and processes for managing enterprise architectures.

  • Increased Efficiency and Cost Savings

TOGAF helps organizations improve their architecture development processes, resulting in greater efficiency and savings. By following the TOGAF Architecture Development Method (ADM), organizations can avoid duplication of work, reduce errors, and optimize resource allocation. This efficiency not only saves time and money, but also ensures that IT resources are effectively used to support business goals.

  • Scalability and Flexibility

TOGAF is designed to be scalable and flexible, making it suitable for organizations of all sizes and industries. Whether you are a small startup or a large enterprise, TOGAF can be tailored to your specific needs and requirements. Its modular structure allows organizations to adapt and evolve their architecture as business needs change, ensuring long-term relevance and sustainability.

  • Industry Recognition and Best Practices

When you implement TOGAF in your organization, you align with industry best practices and standards. TOGAF certification is widely recognized and respected in the field of enterprise architecture, providing a competitive advantage to individuals and organizations alike. By following the TOGAF guidelines and principles, you can ensure that your architectural initiatives are aligned with global standards and best practices.

 

Examples of successful TOGAF implementations in organizations

  1. Global 50 and Fortune 500 Companies: Over 80% of the Global 50 and more than 60% of the Fortune 500 companies have been utilizing TOGAF for nearly two decades, showcasing its widespread adoption and effectiveness in large enterprises.
  2. Large Companies with Hierarchical Structures: TOGAF is well-suited for large companies with hierarchical structures and divisions that aim to implement large enterprise architectures with substantial budgets and resources. Organizations fitting this profile have successfully leveraged TOGAF to align their IT strategies with business objectives and drive architectural excellence.
  3. Enterprises Emphasizing Business Innovation: Organizations that view enterprise architecture as a means to become more agile and innovative have found success with TOGAF. By leveraging TOGAF’s structured approach to architecture development and management, these enterprises have achieved explicit business goals and improved their ability to handle change effectively.
  4. Companies Seeking Efficiency and Cost Reduction: Implementing TOGAF has helped organizations streamline their IT architecture development processes, leading to improved efficiency, reduced errors, and cost savings. By aligning IT with business goals and optimizing architecture development, these companies have achieved tangible benefits from TOGAF implementation.

 

Specific benefits of using TOGAF:

  1. Better Communication and Collaboration: TOGAF facilitates improved communication and collaboration between departments and business units within an organization, enhancing alignment and synergy across different areas.
  2. Improved Understanding of Business Processes: By implementing TOGAF, organizations gain a deeper understanding of their business processes, enabling them to identify inefficiencies, redundancies, and areas for improvement within their operations.
  3. Enhanced Agility and Flexibility: TOGAF provides organizations with the agility and flexibility needed to respond effectively to rapidly changing market conditions and evolving business requirements. This adaptability ensures that enterprises can adjust their architectures to meet new challenges and opportunities.

Conclusion

Overall, implementing TOGAF in your organization can bring many benefits, such as better alignment with business goals, better decision-making and management, greater efficiency and cost savings, scalability and flexibility, and industry recognition. By adopting TOGAF, organizations can optimize their architecture development processes, drive strategic alignment of IT and business goals, and achieve long-term success in today’s rapidly changing business environment. Accepting TOGAF is not just about accepting the framework; It aims to enable your organization to thrive in the digital age and achieve sustainable growth and innovation.

 

]]>
https://blogs.perficient.com/2024/03/29/benefits-of-implementing-togaf-in-your-organization/feed/ 0 360730
Essential Array Methods Guide: Part 2 https://blogs.perficient.com/2024/03/28/essential-array-methods-guide-part-2/ https://blogs.perficient.com/2024/03/28/essential-array-methods-guide-part-2/#respond Thu, 28 Mar 2024 13:07:31 +0000 https://blogs.perficient.com/?p=359523

In continuation of Part 1, we will now delve into essential array methods that enhance your JavaScript toolkit. These methods allow you to effortlessly handle complex data manipulation tasks, opening new possibilities for your projects.

isArray()

isArray() is your trusty detector for identifying arrays. Just pass any value to Array.isArray(), and it quickly tells you whether it’s an array or not. Simple, yet incredibly useful for handling different types of data.

Example:

const animals = ["Cheetah", "Lion", "Zebra", "Horse", "Deer"];
const isArray = Array.isArray(animals);
console.log("Is passed value an array: ", isArray);

Output:
Essential Array 1

keys(), values(), and entries()

These methods give you iterators for keys, values, and key-value pairs within an array. Essentially, they provide different ways for you to move through the elements of an array.

Key

const fruits = ["apple", "banana", "orange"];
for (const key of fruits.keys()) {
  console.log("Keys of an array: ", key);
}

Output:
Essential Array 2

value

const fruits = ["apple", "banana", "orange"];
for (const value of fruits.values()) {
  console.log("Values of an array: ", value);
}

Output:

entries

const fruits = ["apple", "banana", "orange"];
for (const [index, fruit] of fruits.entries()) {
  console.log("Values of array with its index: ", index, fruit);
}

Output:
Array 4

from()

Array.from(): It’s like a magic wand that turns almost anything into an array. Whether you have scattered pieces of data or an iterable object, Array.from() neatly organizes them into a new array, ready for action.

Example:

const arrayLike = { length: 3, 0: "a", 1: "b", 2: "c" };
const newArray = Array.from(arrayLike);
console.log(newArray);

Output:
from()

fill()

The fill() method is used to change all elements in an array to a static value.

Example:

const numbers = [1, 2, 3, 4, 5];
numbers.fill(0, 2, 4); // [1, 2, 0, 0, 5]
console.log(numbers)

Output:
fill()

flat()

Imagine you have a stack of nested trays, each containing some items. flat() is like taking out all the items from those trays and putting them into a single tray, simplifying the organization.

Example:

const nestedArray = [["Peter Pan", "Aladdin"], ["Mulan", "Maleficent"], ["Moana", "Tangled"]];
const flattenedArray = nestedArray.flat();
console.log("flat method:\n",flattenedArray);

Output:
flat()

with()

The with() method is a tool for arrays that lets you change one specific item without touching the original array. It creates a new array reflecting the update.

Syntax

newArray = array.with(index, newValue);

Parameters

  • index: This represents the position of the item that needs to be updated.
  • newValue: The new value for the specified item.

Example:

const originalArray = ['apple', 'banana', 'cherry'];
const newArray = originalArray.with(1, 'grape');

console.log("New Array:",newArray); 
console.log("Orignal Array:",originalArray);

Output:
with()

length

Length tells you how many items are in an array or characters in a string.

Example:

const languages = ["Urdu", "English", "Hindi", "Spanish", "Italian"];

console.log("Length of array:",languages.length);

Output:
length

at()

Get the item located at a particular position within an array or a string, considering positive and negative indexes.

Example:

const languages = ["Urdu", "English", "Hindi", "Spanish", "Italian"];

console.log("Element at end of the array:",languages.at(-1));

Output:
Essential Array 10

join()

The function combines all elements of an array into a single string, separating them with a specified delimiter.

Example:

const series = ["Array", "Methods", "In", "JavaScript"];

console.log("Series:",series.join(' '));

Output:
join()

delete()

Removes a property from an object; it is not typically used for arrays as it leaves undefined holes.

Example:

const languages = ["Urdu", "English", "Hindi", "Spanish", "Italian"];

delete languages[1];

console.log("After deleting element at 1",languages);

Output:
Essential Array 12

spread (…)

This method enables an array or other iterable object to be spread out in locations where functions or arrays anticipate zero or more arguments or elements.

Example:

const languagesOne = ["Urdu", "English"];
const languagesTwo = ["Spanish", "Italian"];
const allLanguages = [...languagesOne, ...languagesTwo]; // Combines languagesOne and languagesTwo into allLanguages
console.log("Combined array:",allLanguages);

Output:
Essential Array 13

toString()

converts all elements of an array into a single string, separating them with commas.

Example:

const languages = ["Urdu", "English", "Hindi", "Spanish", "Italian"];

let languagesString = languages.toString();
console.log("Array to String:",languagesString);

Output:
Essential Array 14

Conclusion:

As we wrap up the essential and primary part of our exploration into JavaScript array methods, remember that these foundational techniques serve as the building blocks for more complex operations. Mastery of adding, removing, and manipulating elements within arrays opens a myriad of possibilities for data handling in your JavaScript projects. Stay tuned for the next part of our series, where we’ll delve into advanced array methods and further expand our JavaScript toolkit.

]]>
https://blogs.perficient.com/2024/03/28/essential-array-methods-guide-part-2/feed/ 0 359523
Essential Array Methods Guide: Part 1 https://blogs.perficient.com/2024/03/27/essential-array-methods-guide-part-1/ https://blogs.perficient.com/2024/03/27/essential-array-methods-guide-part-1/#respond Wed, 27 Mar 2024 16:26:05 +0000 https://blogs.perficient.com/?p=359501

Arrays are a cornerstone of JavaScript programming, offering a versatile way to keep and manage data. In this initial segment, we`ll explore the essential array methods that every JavaScript developer should know. If you’re eager to advance further, keep an eye out for advanced array methods in JavaScript.

Introduction to Essential Array Methods

Arrays are fundamental data structures in JavaScript, allowing collections of objects to be stored and manipulated efficiently. This blog builds the foundation for array conversions in JavaScript and explores important array methods.

push() and pop()

push()

The function adds elements to the end of an array.

Example:

const fruits = ["apple", "banana"];
fruits.push("orange"); 
console.log("Push method\n", fruits);

Output:
Essential Array Methods 1

pop()

Only the last person in the queue leaves when someone leaves from the end. Similarly, the last element is removed from an array and returned by pop().

Example:

const fruits = ["apple", "banana", "orange"];
const removedFruit = fruits.pop();
console.log("Pop method", fruits);

Output:
Essential Array Methods 2

shift() and unshift()

Let’s switch gears and talk about the beginning of the array, where folks arrive and leave first.

shift()

This method is like the first person in line leaving. The function removes the first element from an array and returns it.

Example:

const Movies = ['The Lion King', 'Aladdin', 'The Jungle Book', 'Moana']
const firstMovie = Movies.shift();
console.log('Shift method:\n',Movies)

Output:
Essential Array Methods 3

unshift():

It’s like making room for a brand-new character at the front of a line. Unshift() provides new factors at the beginning of an array.

Example:

const Movies = ['The Lion King', 'Aladdin', 'The Jungle Book', 'Moana']
Movies.unshift('maleficent');
console.log('unshift method:\n',Movies)

Output:
Essential Array Methods 4

concat

This combines arrays, creating a fresh array without altering the originals.

Example:

const Movies1 = ["The Lion King", "Aladdin"];
const Movies2 = ["The Jungle Book", "Moana"];

const combinedMovies = Movies1.concat(Movies2);
console.log("concat method:\n", combinedMovies);

Output:
Essential Array Methods 5

slice()

Picture cutting a slice of pizza without disturbing the rest of it. The slice() method takes out a piece of an array and gives you a new array without affecting the original.

Example:

const vegetables = ["cucumbers", "carrots", "potatoes", "Onions"];
const sliceOfVegetable = vegetables.slice(1, 3);
console.log("slice method:\n", sliceOfVegetable);

Output:
Essential Array Methods 6

splice()

Consider getting a necklace with flowers that you can rearrange or add. splice() allows you to make changes to an array by removing or replacing elements and inserting new ones at specific locations.

Example:

const games = ["Archery", "Base ball", "Cricket"];
games.splice(2, 2, "Dodgeball", "Football");
console.log("splice mathods:\n", games);

Output:
Essential Array Methods 7

toSpliced()

The Array.toSpliced() method creates a new array by removing or replacing elements in an existing array without modifying the original array. Since it’s an immutable operation, the source array doesn’t change. When you wish to work with a changed version of the original array but need to preserve its state, this approach comes in handy.

Syntax

array.toSpliced(start, deleteCount, item1, item2,... itemN)

Parameters

start: The index at which to initiate changing the array. A negative index can be used, counting back from the last item.

deleteCount: items will be deleted from the array, starting from the given start.

item1, item2,…, itemN: Items to add to the array starting from the start position.

Original Tasks List

const tasks = ['Email team', 'Meeting at 2 PM', 'Write report'];

Adding Elements Without Removing

Suppose you want to add two new tasks at the end without removing any existing tasks.

const addedTasks = tasks.toSpliced(3, 0, 'Update project plan', 'Review budgets');

console.log(addedTasks);

Output:
Picture9

Removing Elements Without Adding

Let’s say the meeting has been canceled, and you want to remove it from the list without adding anything new.

const removedTasks = tasks.toSpliced(1, 1);

console.log(removedTasks);

Output:
Picture10

Replacing an Element

If you need to replace ‘Write report’ with ‘Prepare presentation’, here’s how you could do it:

const replacedTask = tasks.toSpliced(2, 1, 'Prepare presentation');

console.log(replacedTask);

Output:
Picture11

Using Negative Indexes

Suppose you want to add a task at the end, but use a negative index to specify the position.

const negativeIndexAdd = tasks.toSpliced(-1, 0, 'Check emails');

console.log(negativeIndexAdd);

Output:
Picture12

Removing and Adding with a Negative Index

Lastly, if you want to replace the last task with two new ones using a negative index,

const negativeIndexReplace = tasks.toSpliced(-1, 1, 'Prepare invoices', 'Send updates');

console.log(negativeIndexReplace);

Output:
Picture13

copyWithin()

method copies a sequence of elements within the same array, effectively overwriting existing elements.

Syntax

array.copyWithin(target, start, end)

Parameters

  • target: the index to copy elements to.
  • start: The index to commence copying elements from.
  • end: (optional) index to stop copying, but not including. If omitted, copyUntil() will copy until the end of the array.

Example:

const array = ['apple', 'banana', 'cherry', 'date', 'elderberry'];

// Copy the elements from index 0 to index 3, overwriting elements starting from index 2
array.copyWithin(2, 0, 3);

console.log(array);

Output:
Picture14

Conclusion

As we conclude the first part of our study of JavaScript essential array methods, keep in mind that these fundamental techniques serve as the foundation for more complicated operations. Stay tuned for the second chapter, where we’ll review more important array functions and broaden our JavaScript toolkit.

]]>
https://blogs.perficient.com/2024/03/27/essential-array-methods-guide-part-1/feed/ 0 359501
useFormState in Next.js: Form Management https://blogs.perficient.com/2024/03/26/useformstate-in-next-js-form-management/ https://blogs.perficient.com/2024/03/26/useformstate-in-next-js-form-management/#respond Tue, 26 Mar 2024 16:09:21 +0000 https://blogs.perficient.com/?p=359720

In my previous blog, we had discussed using server actions with our forms and managing the loading state with the new useFormStatus hook from react-dom. In this one, we are going to explore another experimental hook from react-dom: useFormState.

Concisely, useFormState is a hook to which we provide a function to manipulate form data. The function provides us with two values, the first being the form data value or the manipulated data we get from our provided function. As we know, when developing NextJS apps, we prefer to have our code on the server side. This hook is beneficial since the function we provide to it will be a server function. More on that later.

Using useFormState in your Code

As I have mentioned earlier, this is an experimental hook by react-dom, meaning it will not be available with our usual npm install package installing command. For this, we will have to run the command instruction in our terminal meant to install the experimental version of react and react-dom:

npm install react@experimental react-dom@experimental

After installation of experimental packages, your package.json should have the dependencies as follows:

Useformstate6

Once this has been completed, a file should also be created to inform your project that the experimental utilities will be used if typescript is being used in your project:

Useformstate7

After this, you should be able to import useFormState in your files and use them accordingly. //@ts-ignore” may need to be added right above the import as TypeScript will not recognize this experimental import, but it will still function as intended.

//@ts-ignore
import { useFormState } from "react-dom";

Creating a Simple Form

Let’s create a simple form that accepts a name and an age.

<form action={onSubmit} className="form-control py-3 border-primary border-3">
<Input inputName="username" placeholder="Enter Name" />
    <Input inputName="age" placeholder="Enter Age" />
<SaveButton />
</form>

We will be using the “name” property of HTML’s input tag, which is given value here in our Input component by the “inputName” property, for picking data from the form inputs.

const Input = ({
  inputName,
  placeholder
}: {
  inputName: string;
  placeholder: string;
}) => {
  return (
    <div className="my-2">
      <input
        className="form-control"
        name={inputName}
        type="text"
        placeholder={placeholder}
      />
    </div>
  );
};

This is the output of our basic form so far:

Useformstate1

As for the submit button, this is where we will be using an aspect of useFormStatus, another experimental hook I mentioned earlier, that is the loading state. It is a Boolean value by the name “pending,” which we get from this hook. We can use this for disabling the submit button to avoid multiple form submit calls or to change the text on the button like we have done here:

//@ts-ignore
import { useFormStatus } from "react-dom";

const SaveButton = () => {
  const { pending: isLoading } = useFormStatus();
  return (
    <div className="my-3">
      <button
        disabled={isLoading}
        className="btn btn-warning form-control"
        type="submit"
      >
        {isLoading ? "Saving..." : "Save"}
      </button>
    </div>
  );
};

More on this in my previous blog post on this hook.

As for our form, we will use the form’s action attribute for our submit action. But first, let’s take a look at the action we create for the purpose of form submission.

const [{ username, age, error }, updateProfile] = useFormState(
    saveProfile,
    {
      username: "",
      age: "",
      error: {},
    }
  );

As seen in the above code snippet, useFormState takes two values. The first is the action we want to perform when submitting the form, and the second is the initial state values of the form. It then returns two values in the form of an array, as we see with the useState hook from React. When we execute our submit action, we receive the updated value as the first value, and the second value is a function that mirrors our created function, the “saveProfile” function. We will utilize this new function, “updateProfile,” for our form submission. When the form is submitted, we will get the updated values again in the first value of the returned array, which I have deconstructed here for convenience.

Form Submit Action

Now let’s take a look at the server action that we passed to our experimental new hook:

"use server";

import { Profile} from "./types";

export const saveProfile: (_prevState: Profile, profile: Profile) => void = (
  _prevSate,
  profile
) => profile;

As we can observe from the top of this file, the file is a server file, and therefore, it will execute its action on the server side rather than the client side. This is basically the main reason for the use of this process. Now if we look at our function, it is accepting two parameters, but we only should be passing one parameter value, which would be our form data. So where did this extra parameter come from? It is actually due to the useFormState hook.

Earlier, I stated that we pass a function to the useFormState hook and, in return, receive a value and a function that resembles a copy of the function passed to the hook. So the extra parameter is actually from the new “updateProfile” function we have taken from the hook. And as seen from the first parameter’s name, this is the extra parameter which has the previous state data of our form, since we do not have a need for this, I have appended an underscore to its name to denote it as an unused variable.

Now Let’s Use This With our Form

To do so, we just need to pass the new function we got from the useFormState hook to the form’s action property. By passing FormData of the form with the details of the form to our function, we can retrieve the data using the get function of the FormData.

const username = (formData.get("username") as string) || "";
  const age = (formData.get("age") as string) || "";

Let’s add some validation to our form submit function:

export const saveProfile: (_prevState: Profile, formData: FormData) => Profile = (
  _prevSate,
  formData
) => {
  const username = (formData.get("username") as string) || "";
  const age = (formData.get("age") as string) || "";
  const invalidName = username.length && username.length < 4;
  const invalidAge = age.length && parseInt(age) < 18;
  let error: ProfileError = {};
  if (!username) error.username = "Name cannot be empty";
  if (!age) error.age = "Age cannot be empty";
  if (invalidName) error.username = "Username must be at least 4 characters";
  if (invalidAge) error.age = "Age must be above 18 years";
  if (username && !invalidName) error.username = "";
  if (age && !invalidAge) error.age = "";

  return ({ username, age, error });
};

Typically, when a user enters values into form input fields and clicks on the submit button, we expect the submit action to be on the client side as these actions are performed on the client device. But with the useFormState we have managed to move this logic to the server side as a server action. We can add more complex validations than what we see above using regex or service API for more checks if required.

Adding these error messages to our form along with their respective input fields:

<form action={updateProfile} className="form-control py-3 border-primary border-3">
        <Input inputName="username" placeholder="Enter Name" />
        <div className="text-danger fs-6">{error.username}</div>
        <Input inputName="age" placeholder="Enter Age" />
        <div className="text-danger fs-6">{error.age}</div>
        <SaveButton />
      </form>

Let’s see the result of our form with the error validation:

Empty form submission:

Useformstate2

Invalid Username input:

Useformstate3

Invalid Age input:

Useformstate4

Successful form submission:
Useformstate5

Conclusion

In conclusion, mastering the useFormState hook in React enables efficient form management, offering easy access to form data and dynamic state tracking. Experiment with its features to enhance your form-building skills and deliver seamless user experiences.

 

]]>
https://blogs.perficient.com/2024/03/26/useformstate-in-next-js-form-management/feed/ 0 359720
useNavigate: Navigation React Router v6 https://blogs.perficient.com/2024/03/22/usenavigate-navigation-react-router-v6/ https://blogs.perficient.com/2024/03/22/usenavigate-navigation-react-router-v6/#respond Fri, 22 Mar 2024 10:42:20 +0000 https://blogs.perficient.com/?p=359628

In React͏ development, smooth moving between pages is vital for a seamless͏ user experience. React Router v6 brings out the useNavigate hook for advanced navigation, simplifying navigation logic͏ in functional elements. This ͏blog post will delve into the useNavigate hook, its features, functions, and benefits in React Router v6.

Introducing useN͏avigate

In React Router͏ v6, useNavigate͏ is a hook ͏given ͏by͏ the react-͏r͏oute͏r-dom package. It substitutes͏ the useHistory hook from past versions and provides similar features for moving between routes ͏programmatically. The useNavigate hook gives back a navigation function that helps you navigate to various routes within your app. To learn more about navigation in React Router, check out my other blog post on navigation with useHistory.

How to Use useNavigate

Let’s illustrate the usage of useNavigate with an example:

Step 1: Import useNavigate

First, import the useNavigate hook from the ‘react-router-dom’ package at the top of your functional component file:

import { useNavigate } from 'react-router-dom';

Step 2: Access the navigate Function

Within your functional component, call the useNavigate hook to access the navigate function:

const navigate = useNavigate();

Step 3: Use the navigate Function to Navigate

Then, you can use the navigate function to navigate to different paths in your application. This can be triggered by an action such as clicking a button or based on some other reason triggering this:

import React from "react";
import { useNavigate } from 'react-router-dom';
import Heading from "./Heading";
import "./NavigationDemo.css";

const NavigationDemo = () => {
  
  const navigate = useNavigate();

  const navigateToNewPath = () => {
    navigate('/new-path' , { state: { page: 'New Page' } }); 
  };

  const replaceCurrentPath = () => {
    navigate('/updated-path', { state: { page: 'Updated Page' } }); 
  };
  return (
    <div className="container">
      <Heading>React Router Navigation Demo</Heading>
      <button onClick={navigateToNewPath}>Navigate to New Path</button>
      <button onClick={replaceCurrentPath}>Replace Current Path</button>
    </div>
  );
};

export default NavigationDemo;


In this example, when the Navigate to New Path button is clicked, the navigateToNewPath function is called, which then uses the navigate function to navigate to the ‘/new-path’ route. When the Replace Current Path button is clicked, it replaces the current path with an updated path in the history, that is, to ‘/updated-path’.

Output:
Advanced Navigation in React Router v6: useNavigate.

Features and functions of useNavigate

The useNavigate hook provides several features and functions for navigation management:

navigate(to, options): The main function returned by useNavigate. Goes to the specified route with other routing option(s).

location: A read-only property that returns the current location object containing information about the current URL.

status: A read-write property that allows state data to be passed to the destination.

replace(to, state): A function that replaces the current contents of the history stack with a new representation such as history.replace.

preload(to): A function that preloads a specified method’s code asynchronously, useful for improving client-side communication performance.

Advantages of useNavigate Over useHistory in React Router v6

With the upgrade from React Router v5 to v6, the useNavigate hook replaced the useHistory hook as a more flexible option. This enhancement reflects a deliberate effort to simplify navigation logic in React applications. Here are several key advantages of useNavigate over useHistory:

1. Simplified API

useNavigate provides a simpler API than useHistory, making it easier for developers to perform navigation tasks without directly interacting with the history object.

useHistory approach:

const history = useHistory();
history.push('/home');
history.replace('/login');

useNavigate approach:

const navigate = useNavigate();
navigate('/home'); // Equivalent to history.push
navigate('/login', { replace: true }); // Equivalent to history.replace

2. Related transportation assistance

useNavigate natively supports relative navigation, allowing developers to write more maintainable and flexible navigation logic, especially in nested routing scenarios.

  • In useHistory, navigation of the current path is cumbersome and error-prone, requiring manual creation of the target path.
  • useNavigate facilitates by providing navigational instructions about the current path.

3. Direct integration with the navigation environment

Navigating state on the destination path is made even easier with useNavigate, increasing the ability to share state between paths without URL clutter.

useHistory method:

history.push({ pathname: '/dashboard', state: { Login: true } });

useNavigate  Method:

navigate('/dashboard', { state: { Login: true } });

4. Improved routing practices

useNavigate provides additional methods for handling navigation behavior, such as change, touch, preventScrollReset and other advanced methods, which are neither direct nor intuitive in useHistory

5. Future-proof and recommended strategies

Introducing useNavigate to React Router v6 and ignoring useHistory signals a shift towards a simpler and more efficient way of managing traffic Adoption of useNavigate is encouraged to make your application future-proof and conform to the latest best practices in React development.

6. Enhanced programmability and readability

The useNavigate hook increases programmability by allowing developers to easily navigate programmatically, improving code readability and maintainability. It eliminates the difficulties of travel and makes the designer’s thinking clear.

Handling Relative Paths and Advanced Use Cases

React Router v6’s approach to relative paths and advanced navigation scenarios provides developers with increased flexibility. For example, to navigate relative to the current URL instead of the route hierarchy, developers can manipulate the current location with the URL constructor, facilitating more complex navigation patterns. You can also check this doc for more information.

Moreover, React Router v6 introduces experimental options like unstable_flushSync and unstable_viewTransition, offering developers experimental ways to manage navigation and view transitions. While marked as unstable, these features highlight the ongoing development and potential future capabilities of the React Router.

options.unstable_flushSync

The options.unstable_flushSync option is used to control the timing of state updates during navigation. By default, React Router uses React.startTransition for state updates when navigating between routes. This approach allows React to prioritize different types of updates and can help keep the application responsive even during large updates. However, there are situations where a developer might need the state update to happen synchronously, especially if immediate feedback is necessary after a navigation action.

When unstable_flushSync is set to true, React Router DOM wraps the initial state update for the navigation in a ReactDOM.flushSync call. This makes the update synchronous, ensuring that the state change and associated UI update happen immediately without waiting for other updates.

It is important to word that, because of the potential impact on performance and person experience, this selection must be used sparingly and simplest while definitely essential.

options.unstable_viewTransition

The options.unstable_viewTransition option enables the use of view transitions for a navigation action. This is part of an experimental feature set that provides more control over how visual changes are handled when navigating between routes.

Setting unstable_viewTransition to true indicates that the navigation should trigger a view transition. This can be useful in scenarios where you want to apply specific styles or animations during the route transition. It leverages the document.startViewTransition() API, allowing developers to define a period during which elements from the old and new routes can overlap, enabling smoother transitions.

This option enhances your application’s visual continuity during navigation events. Like unstable_flushSync, unstable_viewTransition is marked as unstable and experimental. It’s part of ongoing efforts to improve the routing experience in web applications but should be used with caution, as its behavior may change in future releases.

Conclusion:

useNavigate is a valuable addition to React Router v6, providing an intuitive and efficient way to manage navigation in active objects. Its features and functionality provide developers with powerful tools to manage navigation logic and enhance the user experience in React applications. By using useNavigate, you can streamline navigation performance and create a seamless browsing experience for your users in React Router v6 applications.

]]>
https://blogs.perficient.com/2024/03/22/usenavigate-navigation-react-router-v6/feed/ 0 359628
useFormStatus FormControl in Next.js https://blogs.perficient.com/2024/03/20/form-control-exploring-useformstatus-in-next-js/ https://blogs.perficient.com/2024/03/20/form-control-exploring-useformstatus-in-next-js/#respond Wed, 20 Mar 2024 05:14:41 +0000 https://blogs.perficient.com/?p=359316

In Next JS, developers prefer most of their code to remain on the server side. However, it is quite tricky to accomplish such feats when you are working with forms. Components that require inputs from the user and thus must be client components to perform user operations like button clicks or text inputs. There useFormStatus comes.

So, let’s embark on this journey together to understand an experimental hook from react-dom: useFormStatus. This hook provides us with the details of the form it uses, like its loading state, the data held by the form at the point of submission, and its method and action.

Adding useFormStatus to your Project

As I have mentioned earlier, this is an experimental hook by react-dom. It will not be available with our usual npm install package installing command.

For this, we will have to run the command instruction in our terminal meant to install the experimental version of react and react-dom:

Installation

npm install react@experimental react-dom@experimental

After installation of experimental packages, your package.json should have the dependencies as follows:

Next5
Once this is done, you should also create a file to let your project know you will be using the experimental utilities if you will be using typescript in your project:
Next6

After this, you should be able to import useFormStatus in your files and use them accordingly. You may have to add “//@ts-ignore” right above the import as typescript will not recognize this experimental import, but it will still work as it is intended to.

//@ts-ignore
import { useFormStatus } from "react-dom";

Creating a Simple Form

Let’s create a simple form with basic test inputs and a submit button to use this hook for its intended purpose.

<form action={onSubmit} className="form-control py-3 border-info">
<ProfileInput inputName="firstName" placeholder="First Name" />
    <ProfileInput inputName="lastName" placeholder="Last Name" />
<SubmitProfile />
</form>

Here, the inputName property of the ProfileInput component is important, as we will give this value to the input’s name property. We will then use that property to grab the values the user will enter in the form.

const ProfileInput = ({
  inputName,
  placeholder
}: {
  inputName: string;
  placeholder: string;
}) => {
  return (
    <div className="my-2">
      <input
        className="form-control"
        name={inputName}
        type="text"
        placeholder={placeholder}
      />
    </div>
  );
};

This is the output of our basic form so far:
Next1

As for the submit button, we will use an aspect of useFormStatus, which is the loading state. We get a Boolean value called “pending” from this hook. We can use this for disabling the submit button to avoid multiple forms submit calls or to change the text on the button like we have done here:

//@ts-ignore
import { useFormStatus } from "react-dom";

const SubmitProfile = () => {
  const { pending: isLoading } = useFormStatus();
  return (
    <div className="my-3">
      <button
        disabled={isLoading}
        className="btn btn-warning form-control"
        type="submit"
      >
        {isLoading ? "Loading..." : "Submit"}
      </button>
    </div>
  );
};

Notice how we do not pass any onSubmit or onClick function to the button? That is because although this is the submit button of our form, we are submitting the form through the action attribute of the html form component. In this way, we can execute a server action when we submit our form, meaning the code snippet that runs during the form submission can be from the server side, which, as I mentioned at the start of this post, is what we want for our Next JS project.

Action Attribute to our Form

Let me show you how the form’s action attribute helps us with this.

const InputForm = () => {
  const [profileList, updateProfileList] = useFormState(updateProfiles, []);

  const onSubmit = async (formData: FormData) => {
    const newProfile = await submitProfileAction(formData);
    updateProfileList(newProfile);
  };
  return (
    <>
      <form action={onSubmit} className="form-control py-3 border-info">
        <ProfileInput inputName="firstName" placeholder="First Name" />
        <ProfileInput inputName="lastName" placeholder="Last Name" />
        <SubmitProfile />
      </form>
    </>
);

In the snippet above, we are passing a function to the action attribute of the form, which usually takes a string path. But here, we can see that we are using the same attribute to execute a function with a parameter that supposedly includes the form’s data. The functions within the onSubmit function are the server actions that we want to execute when the form is submitted, so they will be in a file marked “use server.”

"use server";

import { Profile } from "./types";

export const updateProfiles: (
  oldProfiles: Profile[],
  newProfile: Profile
) => void = (oldProfiles, newProfile) => [...oldProfiles, newProfile];

const createProfile = (profile: Profile) => {
  return new Promise((resolve) => setTimeout(() => resolve(profile), 2000));
};

export const submitProfileAction = async (formData: FormData) => {
  const firstName = formData.get("firstName") as string || '';
  const lastName = formData.get("lastName") as string || '';
  return await createProfile({ firstName, lastName });
};

In the submitProfileAction, we are taking input values of our form with the get property in formData (which we got when we passed onSubmit to the action attribute in the form). This is why we gave values to name attributes in our inputs since we are using the same values from that name attribute to pick the data of the form we want.

This is the fascinating part, and the reason why we are using our form component in this way instead of the traditional onSubmit function passed to the submit button. The ability to perform server actions while using forms is quite beneficial to exploit while working on Next JS projects.

Let’s add some output for the form action:

return (
    <>
      <form action={onSubmit} className="form-control py-3 border-info">
        <ProfileInput inputName="firstName" placeholder="First Name" />
        <ProfileInput inputName="lastName" placeholder="Last Name" />
        <SubmitProfile />
      </form>
      {!!profileList.length && (
        <ul className="bg-light border-success form-control border-3 my-3 p-4">
          {profileList.map(({ firstName, lastName }: Profile) => (
            <li key={crypto.randomUUID()}>{`${firstName} ${lastName}`}</li>
          ))}
        </ul>
      )}
    </>

This should allow us to see how our form performs with server actions and a loading state provided by useFormStatus.

Output

 using useFormStatus

Loading state after form submission

Loading state after form submission using useFormStatus

Result after the form is submitted

Result after the form is submitted using useFormStatus

Conclusion

The useFormStatus hook simplifies form management in Next.js applications by abstracting complexity associated with form state management. This tool enables developers to create more maintainable and user-friendly forms. Streamlining the development process and enhancing the user experience in Next.js applications.

]]>
https://blogs.perficient.com/2024/03/20/form-control-exploring-useformstatus-in-next-js/feed/ 0 359316