Git Product home page Git Product logo

mug's Introduction

Disclaimer: This is not an official Google product.

Mug

A small Java 8 utilities library (javadoc), with 0 deps (Proto, BigQuery, Guava addons are in separate artifacts).

Offers:

  • Intuitive to read, powerful string manipulation (StringFormat, Substring)
    • new StringFormat("/{user}-home/{yyyy}/{mm}/{dd}").parse(path, (user, yyyy, mm, dd) -> ...)
    • String user = before(first('@')).from(email).orElseThrow();
  • Streaming pairs (BiStream)
    • Map<Instant, Long> histogram = zip(times, counts).toMap();
    • Map<K, V> combined = concat(map1, map2).toMap();
    • Map<Principal, V> keyedByPrincipal = BiStream.from(keyedByUserId).mapKeys(UserId::principal).toMap();
  • More (MoreStreams, Optionals, DateTimeFormats, ...)
    • Create Optional with a guard condition:
      • return optionally(count > 0, () -> total / count);
    • Parse any legit date/time string (without a pattern string):
      • Instant timestamp = DateTimeFormats.parseToInstant("2024-01-30 15:30:00-08")
      • DateTimeFormats.formatOf("Tue, 10 Jan 2023 10:00:00.123 America/Los_Angeles")

Installation

Maven

Add the following to pom.xml:

  <dependency>
    <groupId>com.google.mug</groupId>
    <artifactId>mug</artifactId>
    <version>7.2</version>
  </dependency>

Add mug-errorprone to your annotationProcessorPaths:

  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <configuration>
            <annotationProcessorPaths>
              <path>
                <groupId>com.google.errorprone</groupId>
                <artifactId>error_prone_core</artifactId>
                <version>2.23.0</version>
              </path>
              <path>
                <groupId>com.google.mug</groupId>
                <artifactId>mug-errorprone</artifactId>
                <version>7.2</version>
              </path>
            </annotationProcessorPaths>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>

Protobuf utils (javadoc):

  <dependency>
    <groupId>com.google.mug</groupId>
    <artifactId>mug-protobuf</artifactId>
    <version>7.2</version>
  </dependency>

Guava add-ons (with SafeQuery and GoogleSql):

  <dependency>
    <groupId>com.google.mug</groupId>
    <artifactId>mug-guava</artifactId>
    <version>7.2</version>
  </dependency>

Gradle

Add to build.gradle:

  implementation 'com.google.mug:mug:7.2'
  implementation 'com.google.mug:mug-guava:7.2'
  implementation 'com.google.mug:mug-protobuf:7.2'

StringFormat

Extracts structured data from string:

new StringFormat("/users/{user}/.{hidden_file_name}")
    .parse(filePath, (user, fileName) -> ...);
new StringFormat("{hour}:{minute}:{second}.{millis}")
    .parse(“10:26:30.748”, (hour, minute, second, millis) -> ...);

An ErrorProne check is in place to check that the number of lambda parameters and the parameter names match the format string.

This allows you to define StringFormat objects as private class constant, and safely use them many lines away.

Substring

Example 1: strip off a prefix if existent:

String httpStripped = Substring.prefix("http://").removeFrom(uri);

Example 2: strip off any scheme prefix from a uri:

String schemeStripped = Substring.upToIncluding(first("://")).removeFrom(uri);

Example 3: split a string in the format of "name=value" into name and value:

Substring.first('=').split("name=value").map((name, value) -> ...);

Example 4: replace trailing "//" with "/" :

Substring.suffix("//").replaceFrom(path, "/");

Example 5: strip off the suffix starting with a dash (-) character :

last('-').toEnd().removeFrom(str);

Example 6: extract a substring using regex :

String quoted = Substring.first(Pattern.compile("'(.*?)'"), 1)
    .from(str)
    .orElseThrow(...);

Example 7: find the substring between the first and last curly braces ({) :

String body = Substring.between(first('{'), last('}'))
    .from(source)
    .orElseThrow(...);

Stream

BiStream streams pairs of objects.

This class closely mirrors JDK Stream API (the few extra methods of "its own" are very straight-forward). If you are familiar with Jdk stream, learning curve is minimal.

Example 1: to concatenate Maps:

import static com.google.mu.util.stream.BiStream.concat;

Map<AccountId, Account> allAccounts = concat(primaryAccouunts, secondaryAccounts).toMap();

Example 2: to combine two streams:

BiStream.zip(requests, responses)
    .mapToObj(RequestAndResponseLog::new);

Example 3: to build a Map fluently:

Map<DoctorId, Patient> patientsByDoctorId = BiStream.zip(doctors, patients)
    .filter((doctor, patient) -> patient.likes(doctor))
    .mapKeys(Doctor::getId)
    .collect(toMap());

Example 4: to build Guava ImmutableListMultimap fluently:

ImmutableListMultimap<ZipCode, Address> addressesByZipCode = BiStream.from(addresses)
    .mapKeys(Address::getZipCode)
    .collect(ImmutableListMultimap::toImmutableListMultimap);

Example 5: to a Map into sub-maps:

import static com.google.mu.util.stream.BiCollectors.groupingBy;

Map<Address, PhoneNumber> phonebooks = ...;
Map<State, Map<Address, PhoneNumber>> statePhonebooks = BiStream.from(phonebooks)
    .collect(groupingBy(Address::state, Collectors::toMap))
    .toMap();

Example 6: to merge Map entries:

import static com.google.mu.util.stream.BiCollectors.toMap;
import static com.google.mu.util.stream.MoreCollectors.flatteningMaps;

Map<Account, Money> totalPayouts = projects.stream()
    .map(Project::payments)  // Stream<Map<Account, Money>>
    .collect(flatteningMaps(toMap(Money::add)));

Example 7: to apply grouping over Map entries:

import static com.google.mu.util.stream.BiCollectors.toMap;
import static com.google.mu.util.stream.MoreCollectors.flatteningMaps;
import static java.util.stream.Collectors.summingInt;

Map<EmployeeId, Integer> workerHours = projects.stream()
    .map(Project::getTaskAssignments)  // Stream<Map<Employee, Task>>
    .collect(flatteningMaps(toMap(summingInt(Task::hours))));

Example 8: to turn a Collection<Pair<K, V>> to BiStream<K, V>:

BiStream<K, V> stream = RiStream.from(pairs, Pair::getKey, Pair::getValue);

Q: Why not Map<Foo, Bar> or Multimap<Foo, Bar>?

A: Sometimes Foo and Bar are just an arbitrary pair of objects, with no key-value relationship. Or you may not trust Foo#equals() and hashCode(). Instead, drop-in replace your Stream<Pair<Foo, Bar>>/List<Pair<Foo, Bar>> with BiStream<Foo, Bar>/BiCollection<Foo, Bar> to get better readability.

Q: Why not Stream<FooAndBar>?

A: When you already have a proper domain object, sure. But you might find it cumbersome to define a bunch of FooAndBar, PatioChairAndKitchenSink one-off classes especially if the relationship between the two types is only relevant in the local code context.

Q: Why not Stream<Pair<Foo, Bar>>?

A: It's distracting to read code littered with opaque method names like getFirst() and getSecond().

Example 1: to group consecutive elements in a stream:

List<StockPrice> pricesOrderedByTime = ...;

List<List<StockPrice>> priceSequences =
    MoreStreams.groupConsecutive(
            pricesOrderedByTime.stream(), (p1, p2) -> closeEnough(p1, p2), toList())
        .collect(toList());

Example 2: to iterate over Streams in the presence of checked exceptions or control flow:

The Stream API provides forEach() to iterate over a stream, if you don't have to throw checked exceptions.

When checked exception is in the way, or if you need control flow (continue, return etc.), iterateThrough() and iterateOnce() can help. The following code uses iterateThrough() to write objects into an ObjectOutputStream, with IOException propagated:

Stream<?> stream = ...;
ObjectOutput out = ...;
iterateThrough(stream, out::writeObject);

with control flow:

for (Object obj : iterateOnce(stream)) {
  if (...) continue;
  else if (...) return;
  out.writeObject(obj);
}

Example 3: to merge maps:

interface Page {
  Map<Day, Long> getTrafficHistogram();
}

List<Page> pages = ...;

// Merge traffic histogram across all pages of the web site
Map<Day, Long> siteTrafficHistogram = pages.stream()
    .map(Page::getTrafficHistogram)
    .collect(flatteningMaps(groupingBy(day -> day, Long::sum)))
    .toMap();

Optionals

Example 1: to combine two Optional instances into a single one:

Optional<Couple> couple = Optionals.both(optionalHusband, optionalWife).map(Couple::new);

Example 2: to run code when two Optional instances are both present:

Optionals.both(findTeacher(), findStudent()).ifPresent(Teacher::teach);

Example 3: or else run a fallback code block:

static import com.google.mu.util.Optionals.ifPresent;

Optional<Teacher> teacher = findTeacher(...);
Optional<Student> student = findStudent(...);
ifPresent(teacher, student, Teacher::teach)             // teach if both present
    .or(() -> ifPresent(teacher, Teacher::workOut))     // teacher work out if present
    .or(() -> ifPresent(student, Student::doHomework))  // student do homework if present
    .orElse(() -> log("no teacher. no student"));       // or else log

Example 4: wrap a value in Optional if it exists:

static import com.google.mu.util.Optionals.optionally;

Optional<String> id = optionally(request.hasId(), request::getId);

Example 5: add an optional element to a list if present:

static import com.google.mu.util.Optionals.asSet;

names.addAll(asSet(optionalName));

All Optionals utilites propagate checked exception from the the lambda/method references.

mug's People

Contributors

0xflotus avatar bihu1 avatar christianciach avatar dependabot[bot] avatar editor-syntax avatar fanjups avatar fluentfuture avatar jbduncan avatar nakulj avatar porfanid avatar xingyutangyuan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mug's Issues

[Docs] Possible outdated documentation

We have identified 1 possible instance of outdated documentation:

About

This is part of a research project that aims to automatically detect outdated documentation in GitHub repositories. We are evaluating the validity of our approach by identifying instances of outdated documentation in real-world projects.

We hope that this research will be a step towards keeping documentation up-to-date. If this has been helpful, consider updating the documentation to keep it in sync with the source code. If this has not been helpful, consider updating this issue with an explanation, so that we can improve our approach. Thanks!

Consider making the tests follow JUnit 5 idioms more closely

@fluentfuture Currently, the tests use a combination of JUnit 4 and JUnit 5 annotations which don't seem to follow the advice laid out in the JUnit 5 User Guide. I fear that this may make the tests, as they stand, prone to bugs and difficult to maintain over time.

The following sections from the JUnit 5 User Guide look like good starting points for resolving this issue:

Problem with Java 8 compilation

ReadMe says : "A small Java 8 utilities library (javadoc), with 0 deps.", unfortunately compilation with Java 8 gives this error:
"java.lang.UnsupportedClassVersionError: com/google/mu/util/stream/BiStream has been compiled by a more recent version of the Java Runtime (class file version 53.0), this version of the Java Runtime only recognizes class file versions up to 52.0"
In Java 9 or later it works well. Can you get Java 8 support?

Type Mismatch Errors in Maybe.java Class of com.google.mu.util Package

Issue Description:
In the Maybe.java class located within the com.google.mu.util package, several methods exhibit type mismatch errors, which result in red lines appearing in the IntelliJ IDEA editor. This issue affects code readability and could lead to potential runtime errors.

Affected Functions:

public final <X extends Throwable> Stream<T> catching(

The catching method has type-related issues that cause the IntelliJ IDEA editor to display red lines. It appears that the expected type of the returned Stream<T> may not match the actual type inferred by the IDE.

public static <T, E extends Throwable> Stream<Maybe<T, E>> maybeStream(

The maybeStream method also exhibits type mismatch errors. The inferred type of the returned Stream<Maybe<T, E>> may not align with the expected type.

Steps to Reproduce:

Open the Maybe.java class located in the com.google.mu.util package in IntelliJ IDEA.
Navigate to the mentioned functions: catching and maybeStream.
Observe the red lines that indicate type mismatch errors.

Expected Behavior:

The code in the Maybe.java class should have correct type annotations and declarations to ensure that the types inferred by the IDE match the expected types. This will improve code readability and prevent potential runtime issues.

BiStream.sortedBy[Values,Keys] gives unexpected output

Hello,

Sorting a BiStream seems to be giving odd output that looks like a bug. Here is a test case that exhibits the issue:

  @Test
  public void test() {
    BiStream.from(
        ImmutableMap.of(
            "key-a", 1,
            "key-b", 1))
    .mapValues((k, v) -> v * 10)
    .sortedByValues(Integer::compare)
    .forEach((k, v) -> System.out.println(k + " " + v));
  }

I would expect output:

key-a 10
key-b 10

but the printed output is:

key-b 10
key-b 10

Both the sort and the map call are necessary for this to manifest. The same behavior occurs if I use sortedByKeys(String::compareTo). Do you have any suggestions about where things are going wrong?

Thanks!

BiStream equivalent to Stream.findFirst()?

Is there an equivalent to findFirst() that can return an Optional<Entry<K,V>> or a reason that such a method is not included? The workaround I'm using is limit(1).toMap() followed by an Iterables.getOnlyElement(map.entrySet()) but it'd be nice if this was built in!

Thanks for a great library.

The type of toImmutableListMultimap(Function<? super E,? extends Object>, Function<? super E,? extends Object>) from the type ImmutableListMultimap is Collector<E,?,ImmutableListMultimap<Object,Object>>, this is incompatible with the descriptor's return type: Collector<E,?,Object>

I got a red cross while working with eclipse 2019 09

Where ? module mug, src/test/java, com.google.mu.util.stream.BiCollectorsTest.java

@Test public void testFlatMapping_toBiStream() {
    BiStream<String, Integer> salaries = BiStream.of("Joe", 1, "Tom", 2);
    ImmutableListMultimap<String, Integer> result = salaries.collect(
        BiCollectors.flatMapping(
            (k, c) -> BiStream.from(nCopies(c, k), identity(), unused -> c),
            ImmutableListMultimap::toImmutableListMultimap));
    assertThat(result)
        .containsExactly("Joe", 1, "Tom", 2, "Tom", 2)
        .inOrder();
  }

Message : The type of toImmutableListMultimap(Function<? super E,? extends Object>, Function<? super E,? extends Object>) from the type ImmutableListMultimap is Collector<E,?,ImmutableListMultimap<Object,Object>>, this is incompatible with the descriptor's return type: Collector<E,?,Object>

But the build is successful.

Do you get the same error ?

RocksDB with BiStream

Hello,

I'm trying to create a BiStream<byte[], byte[]> from a RocksIterator (RocksDB).

It can be done with two RocksIterators and the BiStream.zip(...) method. But this feels insecure because you have 2 iterator states, which can vary.

Another way is to create a Iterator<Map.Entry<byte[], byte[]>> and wrap it in a Stream<Map.Entry<byte[], byte[]>>. Inside the BiStream class is a constructor and a method (BiStream.form()) for this kind of stream, but both of them are not public.

Leaving us with BiStream.biStream(entryStream).mapKeys(Map.Entry::getKey).mapValues(Map.Entry::getValue) (a short version of this would also be nice). This creates additional intermediate Map.Entry objects, which are unnecessary in this case.

Are there other ways to create a new BiStream?

2.3 availability

Hi, and thanks for this library - I've had a lot of use out of BiStream in particular.

There seem to be quite a few changes (I'm most interested in BiCollectors) which aren't in 2.2, and there don't seem to be any 2.3-SNAPSHOT versions in any snapshot repository, so just wondering whether this will change soon? Thanks, and apologies if I'm missing something here.

Provide OSGi Metadata

It would be beneficial for OSGi users to have this library installed directly in the OSGi runtimes if the OSGi metadata is provided in its MANIFEST.MF.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.