Subscribe To Our Newsletter
You will receive our latest post and tutorial.
Thank you for subscribing!

required
required


Java – Lambda Expression

Lambda Expressions are a significant feature introduced to enable functional programming in the language. Lambda expressions allow you to treat functions as firstclass citizens, making it easier to express behavior as data, and to pass functions as arguments to methods, return them from methods, or assign them to variables. A Lambda Expression is a concise way to represent an anonymous function—a function without a name, often referred to as a “lambda.” It consists of parameters, an arrow ->, and a body.

Syntax of a Lambda Expression:

(parameters) -> { body }

Lambda expressions can be used wherever a functional interface is expected. A functional interface is an interface that contains only one abstract method, often referred to as a “single abstract method” (SAM) interface. These interfaces are also known as functional interfaces because they can be used with lambda expressions.

Lambda expressions greatly improve code readability and provide a more concise way to define simple functions or actions. They are widely used in combination with functional interfaces, making it easier to work with collections, streams, and other functional programming constructs introduced in Java 8 and beyond.

UserRegistration userRegistration = (user) -> {

            // 1. run validations

            if (user == null) {
                throw new RuntimeException("User must not be null.");
            }

            if (user.getEmail() == null) {
                throw new RuntimeException("Email must not be null.");
            }

            // 2. save user to db

            user.setId(faker.number().randomNumber());

            // 3. send welcome email

            return user;
        };

        String firstName = faker.name().firstName();
        String lastName = faker.name().lastName();
        String email = (firstName + lastName).toLowerCase() + "@gmail.com";

        User user = User.builder().firstName(firstName).lastName(lastName).email(email).phoneNumber(faker.phoneNumber().cellPhone()).build();

        System.out.println("User: " + user.toString());

        User signedUpUser = userRegistration.signUp(user);

        System.out.println("Signed Up User: " + signedUpUser);

 

Github Code Reference

July 23, 2021

Java – Stream

The Java Stream API is a powerful addition to the Java programming language introduced in Java 8. It provides a functional-style approach to process data in collections or other sources, making it easier to perform operations on data in a declarative and concise manner. The Stream API allows you to efficiently process large amounts of data using functional programming constructs like map, filter, reduce, and more.

Key characteristics of the Java Stream API:

  1. Stream: A stream is a sequence of data elements that can be processed sequentially or in parallel. It is not a data structure, but rather an abstraction that allows you to process data in a more functional way.
  2. Functional Programming: The Stream API heavily leverages functional programming principles, allowing you to use lambda expressions and method references to define operations on data.
  3. Intermediate and Terminal Operations: Stream operations can be categorized into two types:
    • Intermediate operations: These operations, like filter, map, and sorted, transform a stream into another stream, allowing for chaining of multiple operations.
    • Terminal operations: These operations, like collect, forEach, and reduce, produce a result or side effect, effectively closing the stream.
  4. Lazy Evaluation: The Stream API uses lazy evaluation, meaning intermediate operations are not executed until a terminal operation is called. This allows for more efficient processing, as only the necessary elements are evaluated.
  5. Parallel Processing: The Stream API can take advantage of multi-core processors by allowing you to perform operations in parallel using parallel() or parallelStream(), enhancing performance for large datasets.

 

In Java, the Stream API provides the filter() method, which is used to select elements from a stream that match a given condition or predicate. The filter() operation creates a new stream containing only the elements that satisfy the specified criteria.

Here’s the syntax of the filter() method:

Stream<T> filter(Predicate<? super T> predicate)
  • T is the type of the elements in the stream.
  • Predicate<? super T> is a functional interface that represents the condition to be checked for each element in the stream.

Now, let’s see an example of using the filter() method to filter elements from a stream:

Example:

/**
 * The filter function filters based on a predicate(True/False).
 * 
 * The items that pass the predicate are passed on to the next stream.
 * 
 * Stream<T> filter(Predicate<? super T> predicate)
 */
static void doFilter() {
    System.out.println("doFilter...");

    // filter on id that is greater than 3.

    List<User> filteredUsers = users.stream().filter(user -> {
        return user.getId() > 3;
    }).collect(Collectors.toList());

    filteredUsers.forEach(user -> {
        System.out.println("User: " + user.toString());
    });

    System.out.println("doFilter done!");
}

Stream API provides the map() method, which is used to transform the elements of a stream from one type to another. It takes a Function as an argument, which is a functional interface representing a function that converts one value to another. The map() operation returns a new stream containing the transformed elements.

static void doMap() {

    System.out.println("doMap...");

    List<Student> mappedStudents = users.stream().map(user -> {

        // @formatter:off
        // map user to student
        return Student.builder()
                    .id(user.getId())
                    .firstName(user.getFirstName())
                    .lastName(user.getLastName())
                    .email(user.getEmail())
                    .phoneNumber(user.getPhoneNumber())
                .build();
        // @formatter:on

    }).collect(Collectors.toList());

    mappedStudents.forEach(student -> {
        System.out.println("student: " + student.toString());
    });

    System.out.println("doMap done!");

}

Stream API provides the flatMap() method, which is used to flatten a stream of streams into a single stream. It takes a Function as an argument, which maps each element of the stream to another stream. The flatMap() operation then concatenates or flattens these inner streams into a single output stream. The flatMap() method is particularly useful when you have a stream of collections or arrays and want to operate on the elements of those collections individually rather than on the entire collection as a single element.

static void doFlatMap() {
    System.out.println("doFlatMap...");

    List<List<Integer>> listOfLists = Arrays.asList(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6), Arrays.asList(7, 8, 9));

    // @formatter:off
    // flatten a stream of streams into a single stream
    // flatmap function must return a stream
    List<Integer> flattenedList = listOfLists.stream()
            .flatMap(list -> list.stream())
            .collect(Collectors.toList());
    // @formatter:on

    flattenedList.forEach(number -> {
        System.out.println("number: " + number);
    });

    System.out.println("doFlatMap done!");
}

The Stream API provides the sorted() method, which is used to sort the elements of a stream based on a specified comparator or the natural ordering of the elements. The sorted() method returns a new stream with the elements sorted according to the specified criteria.

//Stream<T> sorted()

//Stream<T> sorted(Comparator<? super T> comparator)
    static void doSort() {
        System.out.println("doSort...");

        List<User> sortedUsers = users.stream()
        // @formatter:off
                .sorted(new Comparator<User>() {
                    @Override
                    public int compare(User u1, User u2) {
                        return u1.getFirstName().compareTo(u2.getFirstName());
                    }
                })
//                .sorted(Comparator.comparingLong(User::getId))
                // @formatter:on
                .collect(Collectors.toList());

        sortedUsers.forEach(user -> {
            System.out.println("user: " + user.toString());
        });

        System.out.println("doSort done!");
    }

doCount()

static void doCount() {

    System.out.println("doCount...");

    long count = users.stream().count();

    System.out.println("count: " + count);

    System.out.println("doCount done!");
}

forEach()

static void doForEach() {
    System.out.println("doForEach...");

    users.forEach(user -> {
        System.out.println("user: " + user.toString());
    });

    System.out.println("doForEach done!");
}

findFirst()

static void doFindFirst() {
    System.out.println("doFindFirst...");

    Optional<User> optUser = users.stream().findFirst();

    optUser.ifPresent(user -> {
        System.out.println("user: " + user.toString());
    });

    System.out.println("doFindFirst done!");
}

Stream API provides the anyMatch() method, which is used to check if any element in the stream satisfies a give condition or predicate. It returns a boolean value indicating whether at least one element in the stream matches the specified condition.

static void doAnyMatch() {
    System.out.println("doAnyMatch...");

    boolean anyMatch = users.stream().anyMatch(user -> user.getFirstName().contains("mine"));

    System.out.println("anyMatch: " + anyMatch);

    System.out.println("doAnyMatch done!");
}

 Stream API provides the allMatch() method, which is used to check if all elements in the stream satisfy a given condition or predicate. It returns a boolean value indicating whether every element in the stream matches the specified condition.

static void doAllMatch() {
    System.out.println("doAllMatch...");

    boolean allMatch = users.stream().allMatch(user -> user.getFirstName().contains("a"));
    System.out.println("allMatch" + allMatch);

    System.out.println("doAllMatch done!");
}

Stream API provides the distinct() method, which is used to remove duplicate elements from a stream. It returns a new stream containing only the distinct elements based on their natural order (if available) or their implementation of equals() method.

static void doDistinct() {
    System.out.println("doDistinct...");

    users.stream().distinct().forEach(user -> {
        System.out.println("user: " + user.toString());
    });

    System.out.println("doDistinct done!");
}

doLimit()

static void doLimit() {
    System.out.println("doDistinct...");

    users.stream().limit(3).forEach(user -> {
        System.out.println("user: " + user.toString());
    });

    System.out.println("doDistinct done!");
}

doParallelStream()

static void doParallelStream() {
    System.out.println("doParallelStream...");

    users.parallelStream().forEach(user -> {
        System.out.println("user: " + user.toString());
    });

    System.out.println("doParallelStream done!");
}

Stream API provides the max() method, which is used to find the maximum element of a stream based on a given comparator or the natural order of the elements. The max() method returns an Optional<T> that contains the maximum element, or an empty Optional if the stream is empty.

static void doMax() {
    System.out.println("doMax...");

    Optional<User> maxUser = users.stream().max(new Comparator<User>() {
        @Override
        public int compare(User u1, User u2) {
            return u1.getFirstName().compareTo(u2.getFirstName());
        }
    });

    maxUser.ifPresent(user -> {
        System.out.println("Max User: " + user.toString());
    });

    System.out.println("doMax done!");
}

Stream API provides the min() method, which is used to find the minimum element of a stream based on a given comparator or the natural order of the elements. The min() method returns an Optional<T> that contains the minimum element, or an empty Optional if the stream is empty.

static void doMin() {
    System.out.println("doMin...");

    Optional<User> maxUser = users.stream().min(new Comparator<User>() {
        @Override
        public int compare(User u1, User u2) {
            return u1.getFirstName().compareTo(u2.getFirstName());
        }
    });

    maxUser.ifPresent(user -> {
        System.out.println("Min User: " + user.toString());
    });

    System.out.println("doMin done!");
}

 

Github Code Repository

 

July 21, 2021

Elasticsearch Sorting

By default, search results are returned sorted by relevance, with the most relevant docs first.

Relevance Score

The relevance score of each document is represented by a positive floating-point number called the _score. The higher the _score, the more relevant the document.

A query clause generates a _score for each document. How that score is calculated depends on the type of query clause. Different query clauses are used for different purposes: a fuzzy query might determine the _score by calculating how similar the spelling of the found word is to the original search term; a terms query would incor‐ porate the percentage of terms that were found. However, what we usually mean by relevance is the algorithm that we use to calculate how similar the contents of a full- text field are to a full-text query string.

The standard similarity algorithm used in Elasticsearch is known as term frequency/ inverse document frequency, or TF/IDF, which takes the following factors into account. The more often, the more relevant. A field containing five mentions of the same term is more likely to be relevant than a field containing just one mention.

Order

Sorting allows you to add one or more sorts on specific fields. Each sort can be reversed(ascending or descending) as well. The sort is defined on a per field level, with special field name for _score to sort by score, and _doc to sort by index order.

The order option can have either asc or desc.

The order defaults to desc when sorting on the _score, and defaults to asc when sorting on anything else.

GET users/_search
{
     "query" : {
            "filtered" : {
                "filter" : { "term" : { "id" : 1 }}
            }
     },
     "sort": { "date": { "order": "desc" }}
}

Perhaps we want to combine the _score from a query with the date, and show all matching results sorted first by date, then by relevance.

GET /_search
{
   "query" : {
            "filtered" : {
                "query":   { "match": { "description": "student" }},
                "filter" : { "term" : { "id" : 2 }}
            }
   }, 
   "sort": [
            {
             "date": {"order":"desc"}
             },
            { 
              "_score": { "order": "desc" }
            }
   ]
}

Order is important. Results are sorted by the first criterion first. Only results whose first sort value is identical will then be sorted by the second criterion, and so on. Multilevel sorting doesn’t have to involve the _score. You could sort by using several different fields, on geo-distance or on a custom value calculated in a script.

Elasticsearch supports sorting by array or multi-valued fields. The mode option controls what array value is picked for sorting the document it belongs to. The mode option can have the following values.

min Pick the lowest value.
max Pick the highest value.
sum Use the sum of all values as sort value. Only applicable for number based array fields.
avg Use the average of all values as sort value. Only applicable for number based array fields.
median Use the median of all values as sort value. Only applicable for number based array fields.

The default sort mode in the ascending sort order is min — the lowest value is picked. The default sort mode in the descending order is max — the highest value is picked.

Note that filters have no bearing on _score, and the missing-but-implied match_all query just sets the _score to a neutral value of 1 for all documents. In other words, all documents are considered to be equally relevant.

 

Sorting Numeric Fields

For numeric fields it is also possible to cast the values from one type to another using the numeric_type option. This option accepts the following values: ["double", "long", "date", "date_nanos"] and can be useful for searches across multiple data streams or indices where the sort field is mapped differently.

Geo Distance Sorting

Sometimes you want to sort by how close a location is to a single point(lat/long). You can do this in elasticsearch.

GET elasticsearch_learning/_search
{
"sort":[{
  "_geo_distance" : {
    "addresses.location" : [
      {
        "lat" : 40.414897,
        "lon" : -111.881186
      }
    ],
    "unit" : "m",
    "distance_type" : "arc",
    "order" : "desc",
    "nested" : {
      "path" : "addresses",
      "filter" : {
        "geo_distance" : {
          "addresses.location" : [
            -111.881186,
            40.414897
          ],
          "distance" : 1609.344,
          "distance_type" : "arc",
          "validation_method" : "STRICT",
          "ignore_unmapped" : false,
          "boost" : 1.0
        }
      }
    },
    "validation_method" : "STRICT",
    "ignore_unmapped" : false
  }
}]
}

 

/**
 * https://www.elastic.co/guide/en/elasticsearch/reference/7.x/query-dsl-nested-query.html<br>
 * https://www.elastic.co/guide/en/elasticsearch/reference/7.3/search-request-body.html#geo-sorting<br>
 * Sort results based on how close locations are to a certain point.
 */
@Test
void sortQueryWithGeoLocation() {

    int pageNumber = 0;
    int pageSize = 10;

    SearchRequest searchRequest = new SearchRequest(database);
    searchRequest.allowPartialSearchResults(true);
    searchRequest.indicesOptions(IndicesOptions.lenientExpandOpen());

    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    searchSourceBuilder.from(pageNumber * pageSize);
    searchSourceBuilder.size(pageSize);
    searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
    /**
     * fetch only a few fields
     */
    searchSourceBuilder.fetchSource(new String[]{"id", "firstName", "lastName", "rating", "dateOfBirth", "addresses.street", "addresses.zipcode", "addresses.city"}, new String[]{""});

    /**
     * Lehi skate park: 40.414897, -111.881186<br>
     * get locations/addresses close to skate park(from a radius).<br>
     */

    searchSourceBuilder.sort(new GeoDistanceSortBuilder("addresses.location", 40.414897,
            -111.881186).order(SortOrder.DESC)
           .setNestedSort(
                   new NestedSortBuilder("addresses").setFilter(QueryBuilders.geoDistanceQuery("addresses.location").point(40.414897, -111.881186).distance(1, DistanceUnit.MILES))));
    
    log.info("\n{\n\"sort\":{}\n}", searchSourceBuilder.sorts().toString());

    searchRequest.source(searchSourceBuilder);

    searchRequest.preference("nested-address");

    try {
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        log.info("hits={}, isTimedOut={}, totalShards={}, totalHits={}", searchResponse.getHits().getHits().length, searchResponse.isTimedOut(), searchResponse.getTotalShards(),
                searchResponse.getHits().getTotalHits().value);

        List<User> users = getResponseResult(searchResponse.getHits());

        log.info("results={}", ObjectUtils.toJson(users));

    } catch (IOException e) {
        log.warn("IOException, msg={}", e.getLocalizedMessage());
        e.printStackTrace();
    } catch (Exception e) {
        log.warn("Exception, msg={}", e.getLocalizedMessage());
        e.printStackTrace();
    }

}

 

Query with explain

Adding explain produces a lot of output for every hit, which can look overwhelming, but it is worth taking the time to understand what it all means. Don’t worry if it doesn’t all make sense now; you can refer to this section when you need it. We’ll work through the output for one hit bit by bit.

GET users/_search?explain
{
   "query" :{"match":{"description":"student"}} }
}

Producing the explain output is expensive. It is a debugging tool only. Don’t leave it turned on in production.

Fielddata

To make sorting efficient, Elasticsearch loads all the values for the field that you want to sort on into memory. This is referred to as fielddata. Elasticsearch doesn’t just load the values for the documents that matched a particular query. It loads the values from every docu‐ ment in your index, regardless of the document type.

The reason that Elasticsearch loads all values into memory is that uninverting the index from disk is slow. Even though you may need the values for only a few docs for the current request, you will probably need access to the values for other docs on the next request, so it makes sense to load all the values into memory at once, and to keep them there.

All you need to know is what fielddata is, and to be aware that it can be memory hungry. We will talk about how to determine the amount of memory that fielddata is using, how to limit the amount of memory that is available to it, and how to preload fielddata to improve the user experience.

Source Code on Github

 

March 21, 2021

Python – Function

A function is a group of related statements or commands that performs a specific task. Functions help break our program into smaller and modular chunks. As our program grows larger and larger, functions make it more organized and manageable. It avoids repetition and makes the code reusable. It only runs when it is called.

Syntax

def function_name(parameters):
    statements

You can pass data, known as parameters, into a function. The terms parameter and argument can be used for the same thing: information that are passed into a function. A parameter is the variable listed inside the parentheses in the function definition. An argument is the value that is sent to the function when it is called. By default, a function must be called with the correct number of arguments. Meaning that if your function expects 2 arguments, you have to call the function with 2 arguments, not more, and not less.

A function can return data as a result.

def function_name(parameters):
    statements
    return value;

Arbitrary Arguments with *args

If you do not know how many arguments that will be passed into your function, add a * before the parameter name in the function definition. This way the function will receive a tuple of arguments, and can access the items accordingly

def print_profile(*profile):
    print(f'name: {profile[0]}, age: {profile[1]}')

print_profile("Folau", 30)

You can also send arguments with the key = value syntax. This way the order of the arguments does not matter.

def say_hi(name):
    print(f'Hi {name}')

say_hi(name="Folau")

Arbitrary Keyword Arguments with **

If you do not know how many keyword arguments that will be passed into your function, add two asterisk: ** before the parameter name in the function definition. This way the function will receive a dictionary of arguments, and can access the items accordingly

def print_user(**user):
    print(f'name: {user["name"]}, age: {user["age"]}')

 

How to call a function?

Once we have defined a function, we can call it from another function, program or even the Python prompt. To call a function, use the function name followed by parenthesis

def say_hi(name):
    print(f'hi {name}')

say_hi('Folau')

The pass statement

function definitions cannot be empty, but if you for some reason have a function definition with no content, put in the pass statement to avoid getting an error.

def say_hi():
   pass

 

Lambda

Lambda is an anonymous function that is defined without a name. Lambda functions are defined using the lambda keyword. A lambda function can take any number of arguments, but can only have one expression.

Syntax

lambda arguments: expression

The expression is evaluated and returned. Lambda functions can be used wherever function objects are required.

doubleNumber = lambda num : num * 2

multiply = lambda num1, num2 : num1 * num2


# Lambda function
doubledNum = doubleNumber(3);
print(doubledNum) # 6

# Lambda function
result = multiply(3,3);
print(result)# 9

Uses of Lambda

We use lambda functions when we require a nameless function for a short period of time. We generally use it as an argument to a higher-order function (a function that takes in other functions as arguments). Lambda functions are used along with built-in functions like filter()map() etc.

numbers = [1,2,3,4,5,6]
new_numbers = list(filter(lambda x: (x%2==0), numbers))
print(f'{new_numbers}') #[2, 4, 6]

 

Module

Modules refer to a file containing Python statements and definitions. A file containing Python code, for example: user.py, is called a module, and its module name would be user. We use modules to break down large programs into small manageable and organized files. Furthermore, modules provide reusability of code. We can define our most used functions in a module and import it, instead of copying their definitions into different programs.

How to use a module?

We can import the definitions inside a module to another module or the interactive interpreter in Python. We use the import keyword to do this. To import our previously defined module user, we type the following in the Python prompt. Using the module name we can access the function using the dot . operator

import user

user.say_hi()

Python has tons of standard modules. You can check out the full list of Python standard modules and their use cases. These files are in the Lib directory inside the location where you installed Python.

Import but rename or give it alias

import user as u

from module import statement

# just one function
from user import say_hi

# import all functions
from user import *

Source code on Github

March 8, 2021

Python – Iteration(for/while loops)

For Loop

for loop is used for iterating over a sequence which can be a list, tuple, dictionary, set, or a string. Iterating is also known as traversing. Loop continues until we reach the last item in the sequence. The body of for loop is separated from the rest of the code using indentation.

Syntax

for val in sequence:
    # do something with val

Example

# list
numbers = range(1,10, 2)

for number in numbers:
    print(number)

# string
message = "Hello World"

for character in message:
    print(f'char: {character}')

# tuple
""" id, person info """
person = tuple(('12345',{"name":"Folau","grade":"3.5"}))
for p in person:
    print(p)

# set
names = set(('Folau','Lisa','Mele'))

for name in names:
    print(name)

#dictionary
profile = dict(name="Folau",age=30,job="SWE")
for attr in profile:
    print(attr)

 

The for loop does not require an indexing variable to set beforehand.

With the break statement we can stop the loop before it has looped through all the items. The example below stops its execution when count is 3.

numbers = range(1,10, 2)

count = 0;
for number in numbers:
    print(number)
    if count == 3:
       break
    count++

With the continue statement we can stop the current iteration of the loop, and continue with the next. The example below shows that when number is 0 the program will throw an error but instead of terminating the program it should keep going til the end of the list.

numbers = range(1,10, 2)

for number in numbers:
    print(number)
    try:
      result = 12 / number
    except:
      continue

 

for loops cannot be empty, but if you for some reason have a for loop with no content, put in the pass statement to avoid getting an error.

numbers = range(1,10, 2)

for number in numbers:
    pass

else statement with for loop

We can use else statement with for loop to execute some code when the for loop is finished. It’s useful in logging or sending a notification when the processing of a sequence is successfully completed.

teams = ['Lakers','Jazz','Suns']
for team in teams:
    print(team)
else:
    print("done looping through teams")

Reverse iteration using reversed function

The for loop iterates through the sequence elements in the order of its occurrence. Sometimes we have to iterate through the elements in the reverse order. We can use reversed() function with the for loop to achieve this.

numbers = (1, 2, 3, 4, 5)

for num in reversed(numbers):
    print(num)

 

While Loop

while loop is used to repeat a block of code until the specified condition is False. It is used when we don’t know the number of times the code block has to execute. We should take proper care in writing while loop condition if the condition never returns False, the while loop will go into the infinite loop. Every object in Python has a boolean value. If the value is 0 or None, then the boolean value is False. Otherwise, the boolean value is True. We can define an object boolean value by implementing __bool__() function. We use the  reserved keyword  – while – to implement the while loop in Python. We can terminate the while loop using the break statement. We can use continue statement inside while loop to skip the code block execution. Python supports nested while loops.

Syntax

while condition:
    # while body
else:
    # else body

Example

count = 0
while count < 10:
    print(count)
    count+=1
else:
    print("done counting!")

Python for loop and while loops are enough to create any type of loops. We can use the break and continue statements to control the loop execution flow. You can also use the “else” block for logging successful execution of the loops.

 

Source code on Github

March 8, 2021