0.2.3 • Published 3 years ago

crystalline v0.2.3

Weekly downloads
3
License
MIT
Repository
github
Last release
3 years ago

Crystalline

A modern utility library with a strong emphasis on readability. Make your code crystal clear.

Inspired by Jest Matchers.

Table of Contents

Introduction

The problem

Manipulating data is part and parcel of developing software, but the resulting code can quickly become difficult to read. You want to minimise the complexity of your codebase to ensure it's doing what you intended and have the confidence to make changes in the future.

The solution

Jest Matchers help make your tests easier to reason about. Crystalline takes this approach and applies it to your application code. It is a library of highly comprehensible functions that perform operations commonly found in code.

Guiding Principles

1) Readable code is maintainable code. 2) Write code as if you were writing a sentence. 3) Don't reinvent the wheel when a readable native solution already exists. 4) Favour brevity but not at the expense of readability.

Installation

NPM:

npm install crystalline

Yarn:

yarn add crystalline

Usage

Import specific modules to reduce the size of your bundle:

// ECMAScript modules
import { sort } from 'crystalline/arrays/sort';

// CommonJS
const { sort } = require('crystalline/arrays/sort');  

sort(...);

Alternatively you can import the whole library:

// ECMAScript modules
import crystalline from 'crystalline';

// CommonJS
const crystalline = require('crystalline').default;

crystalline.arrays.sort(...);

API Reference

The library organises its functions into categories based on the type of variable they primarily operate on. All functions within a category expect that type of variable as their first parameter. Functions are always pure. Vocabulary is reused across categories to reduce the learning curve.

arrays

  • alter

    • const input = ["a", "b", "c", "d"];
      
      const result = alter(input)
        .byApplying((n) => n.toUpperCase())
        .atIndex(1);
      
      expect(result).toEqual(["a", "B", "c", "d"]);
      const input = ["b", "n", "n", "s"];
      
      const result = alter(input).byInsertingBetweenEachItem("a");
      
      expect(result).toEqual(["b", "a", "n", "a", "n", "a", "s"]);
    • const input = ["a", "b", "c", "d", "e", "f"];
      
      const result = alter(input).byMovingItemAtIndex(0).toIndex(2);
      
      expect(result).toEqual(["b", "c", "a", "d", "e", "f"]);
    • const input = ["a", "b", "c", "d", "e", "f"];
      
      const result = alter(input).byMovingItemAtIndex(2).toTheStart();
      
      expect(result).toEqual(["c", "a", "b", "d", "e", "f"]);
    •  const input = ["a", "b", "c", "d", "e", "f"];
      
       const result = alter(input).byMovingItemAtIndex(2).toTheEnd();
      
       expect(result).toEqual(["a", "b", "d", "e", "f", "c"]);
      const input1 = [1, 1, 2, 1];
      const input2 = [1, "1"];
      const input3 = [[42], [42]];
        
      const result1 = alter(input1).byRemovingDuplicates();
      const result2 = alter(input2).byRemovingDuplicates();
      const result3 = alter(input3).byRemovingDuplicates();
        
      expect(result1).toEqual([1, 2]);
      expect(result2).toEqual([1, "1"]); 
      expect(result3).toEqual([[42]]);
    • const input = [1, 2, 3, 4, 5, 6, 7, 8];
      
      const result = alter(input).byRemovingItemsBetweenIndex(2).andIndex(3);
      
      expect(result).toEqual([1, 2, 6, 7, 8]);
    • const input = [1, 2, 3, 4, 5, 6, 7, 8];
      
      const result = alter(input).byRemovingItemsBetweenIndex(3).andTheEnd();
      
      expect(result).toEqual([1, 2, 3]);
      const input = [1, 2, 1, 3, 4];
      
      const result = alter(input).byRemovingItemsEqualTo(1, 2);
      
      expect(result).toEqual([3, 4]);
      const input = [
        "a",
        false,
        "b",
        null,
        "c",
        undefined,
        "d",
        0,
        "e",
        -0,
        "f",
        NaN,
        "g",
        "",
      ];
      
      const result = alter(input).byRemovingFalsyItems();
      
      expect(result).toEqual(["a", "b", "c", "d", "e", "f", "g"]);
  • findItemsIn

    const input1 = [1, 2, 3, 4];
    const input2 = [7, 6, 5, 4, 3];
    
    const result = findItemsIn(input1).containedIn(input2);
    
    expect(result).toEqual([3, 4]);
    const input1 = [1, 2, 3, 4];
    const input2 = [7, 6, 5, 4, 3];
    
    const result = findItemsIn(input1).notContainedIn(input2);
    
    expect(result).toEqual([1, 2]);
    const input1a = [1, 2, 3, 4];
    const input1b = [7, 6, 5, 4, 3];
    
    const result = findItemsIn(input1a).and(input1b).thatAreUnique();
    
    expect(result).toEqual([1, 2, 7, 6, 5]);
  • from

     const result = from(input).pickQuantity(2).fromTheStart();
     
     expect(result).toEqual(["foo", "bar"]);
     ```
    
     </p>
     </details>
    • const result = from(input) .pickWhile((n) => n !== 4) .fromTheStart();

      expect(result).toEqual(1, 2, 3);

      </p>
      </details>
    • const result = from(input)
        .pickWhile((n) => n !== 4)
        .fromTheEnd();
      
      expect(result).toEqual([3, 2, 1]);
      </p>
      </details>
      
      </p>
      </details>
      
      <details>
      <summary><code>pickFirst</code></summary>
      <p>           
      <br/>          
      Return the first item from the input array.
               
      ```javascript
      const result = from(["fi", "fo", "fum"]).pickFirst();
      
      expect(result).toBe("fi");

      expect(result).toBe("fum");

       
      </p>
      </details>
      
      <details>
      <summary><code>dropQuantity</code></summary>
      <p>
              
    • const result = from(input).dropQuantity(2).fromTheStart();

      expect(result).toEqual("baz");

      </p>
      </details>
    • expect(result).toEqual("foo");

      </p>
      </details>
      
      </p>
      </details>
      
      <details>
      <summary><code>dropWhile</code></summary>
      <p>
           
    • expect(result).toEqual(3, 4, 3, 2, 1);

      </p>
      </details>
    • const result = from(input) .dropWhile((n) => n <= 3) .fromTheEnd();

      expect(result).toEqual(1, 2, 3, 4);

      </p>
      </details>
      
      </p>
      </details>
      
      <details>
      <summary><code>dropFirst</code></summary>
      <p>           
      <br/>          
      Create a new array containing every item from the input array except the first.
               
      ```javascript
      const result = from(["fi", "fo", "fum"]).dropFirst();
      
      expect(result).toEqual(["fo", "fum"]);

      const result = from(input).dropConsecutiveRepeats();

      expect(result).toEqual(1, 2, 3, 4, 2);

       
      </p>
      </details>
      
      <details>
      <summary><code>dropConsecutiveRepeatsSatisfying</code></summary>
      <p>           
      <br/>          
      Create a new array containing every item from the input array with any consecutive elements satisfying the predicate removed.
                  
      ```javascript
      const input = [1, -1, 1, 3, 4, -4, -4, -5, 5, 3, 3];
      
      const result = from(input).dropConsecutiveRepeatsSatisfying(
        (x, y) => Math.abs(x) === Math.abs(y)
      );
      
      expect(result).toEqual([1, 3, 4, -5, 3]);
  • sort

    const result = sort(input).ascendingByProperty("age");

    expect(result).toEqual( { name: "Mikhail", age: 62 }, { name: "Emma", age: 70 }, { name: "Peter", age: 78 }, );

     
    </p>
    </details>
    
    <details>
    <summary><code>descendingByProperty</code></summary>
    <p>           
    <br/>          
    Create a new array with items from the input array sorted in descending order by a given property.
                
    ```javascript
    const input = [
      { name: "Emma", age: 70 },
      { name: "Peter", age: 78 },
      { name: "Mikhail", age: 62 },
    ];
    
    const result = sort(input).descendingByProperty("age");
    
    expect(result).toEqual([
      { name: "Peter", age: 78 },
      { name: "Emma", age: 70 },
      { name: "Mikhail", age: 62 },
    ]);
     </p>
     </details>
    • const input = clara, bob, alice;

      const result = sort(input) .firstDescendingByProperty("age") .thenAscendingByProperty("name");

      expect(result).toEqual(alice, clara, bob);

      </p>
      </details>
    • const result = sort(input) .firstDescendingByProperty("age") .thenDescendingByProperty("name");

      expect(result).toEqual(clara, alice, bob);

      </p>
      </details>
      
      </p>
      </details>
  • split

    const result = split(input).atFirstEncounterOf((n) => n === 2);

    expect(result).toEqual([1, 2, 3, 1, 2, 3]);

     
    </p>
    </details>
    
    <details>
    <summary><code>atIndex</code></summary>
    <p>           
    <br/>          
    Create a new array that contains two arrays after splitting the original at the index specified.
                
    ```javascript
    const input = [1, 2, 3];
    
    const result = split(input).atIndex(1);
    
    expect(result).toEqual([[1], [2, 3]]);
  • tally

    const result = tally(input).byApplying(Math.floor);

    expect(result).toEqual({ 1: 3, 2: 2, 3: 1 });

     
    </p>
    </details>    

objects

  • alter

     const result = alter(input)
       .byApplying((n) => n.trim())
       .toKey("firstName");
    
     expect(result).toEqual({
       firstName: "Tomato",
       data: { elapsed: 100, remaining: 1400 },
       id: 123,
     });
     ```
    
     </p>
     </details>
  • copy

    const result = copy(input).deeply();

    expect(input).toEqual(result);

    // Referential checks expect(input !== result).toBe(true); expect(input.a !== result.a).toBe(true); expect(input.c !== result.c).toBe(true);

     
    </p>
    </details>   
    
    <details>
    <summary><code>discardKeys</code></summary>
    <p>           
    <br/>          
    Create a partial copy of the object omitting the keys specified. 
                
    ```javascript
    const input = { a: 1, b: 2, c: 3, d: 4 };
    
    const result = copy(input).discardKeys("a", "d");
    
    expect(result).toEqual({ b: 2, c: 3 });

    const result = copy(input).keepKeys("a", "c");

    expect(result).toEqual({ a: 1, c: 3 });

     
    </p>
    </details>   
  • merge

     const result = merge(obj1)
       .deeplyWith(obj2)
       .resolvingConflictsViaFirstObject();
    
     expect(result).toEqual({
       name: "fred",
       age: 10,
       hair: "blonde",
       contact: { email: "moo@example.com" },
     });
     ```
    
     </p>
     </details>
     
    • const result = merge(obj1) .deeplyWith(obj2) .resolvingConflictsViaSecondObject();

      expect(result).toEqual({ name: "fred", age: 40, hair: "blonde", contact: { email: "baa@example.com" }, });

      </p>
      </details>
    • const result = merge(obj1) .deeplyWith(obj2) .resolvingConflictsByApplying((x, y) => ...x, ...y);

      expect(result).toEqual({ a: true, b: true, c: { values: 10, 20, 15, 35 }, });

      </p>
      </details>
      
      </p>
      </details> 

numbers

  • clamp

misc

  • sequenceFrom

     const result = sequenceFrom(rule)
       .startingWith(seed)
       .untilCondition(terminator);
    
     expect(result).toEqual([10, 100, 10000, 100000000]);
     ```
    
     </p>
     </details>

Contributing

Thank you for thinking about contributing to Crystalline, we welcome all feedback and collaboration from the community. We don't want the process to be laborious, so we've kept our contributing guide reeeeally short. Please take a moment to read through it as doing so will help ensure the library remains consistent as it grows.

Contributing Guide