npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@akoskovacs/keyed-list

v1.2.0

Published

Immutable keyed list collection

Downloads

22

Readme

keyed-list.ts

Efficiently store and retreive list elements based on their id key. This approach has the same benefits as having both an array and a hash for the same list.

With the array we can simply iterate through the elements or use them by their index. And with the hash, we can efficiently retrieve them, just by the id. Although, this implementation will only store the ids in the array to conserve space. But, this mechanism is completely transparent, the user don't have to think about it.

The immutability makes this library easy to use with React or other functional libraries and is also less error-prone.

Installation

The preferred way is to add it to the project locally as a dependency:

npm install --save @akoskovacs/keyed-list

Or via yarn:

yarn add @akoskovacs/keyed-list

Usage

The list element must include a unqiue id property. The id can be a GUID given by an API. It then can be used to efficiently address the elements. Take this example:

import keyedList from '@akoskovacs/keyed-list';

interface PostItem {
  id: string;
  author: string;
  content: string;
  createdAt: Date;
};

const posts: PostItem[] = [
  {
    id: '1234',
    author: 'John',
    votes: 4,
    content: 'Hello, world!',
    createdAt: new Date('2020-12-11 10:45:21')
  },
  {
    id: '2222',
    author: 'Steve',
    votes: 0,
    content: 'Second post',
    createdAt: new Date('2020-12-11 14:12:55')
  },
  {
    id: '4242',
    author: 'Peter',
    votes: 2,
    content: 'This is not the end',
    createdAt: new Date('2020-12-11 14:12:55')
  }
];

After the API gave back the elements the library can start manipulating the data, with the following functions:

| Function name | Description | Example | |-------------------|------------------------------------------------------|------------------------------------------------------------------------------------------| | fromArray | Create a list from an array | const list = keyedList.fromArray(posts); | | toArray | Converts the list back into an array | const newPosts = keyedList.toArray(list); | | getIds | Retrieve multiple elements by their ids | const ids = keyedList.getIds(list); | | getById | Retrieve the element by its id | const steve = keyedList.getById(list, '2222'); | | getByIds | Converts the list back into an array | const sp = keyedList.getByIds(list, ['4242, '2222']); | | getIdByIndex| Retrieve the id by the element's numeric index | const firstId = keyedList.getIdByIndex(list, 0); | | getFirst | Gets the first element in the list | const first = keyedList.getFirst(list); | | getLast | Gets the last elemenet in the list | const last = keyedList.getLast(list); | | getByIndex | Gets the element by its index | const second = keyedList.getByIndex(list, 1); | | getCount | Get the length of the list | const count = keyedList.getCount(list); | | update | Update properties of given list element | const newList = keyedList.update(list, { id: '4242', votes: 1 }); | | append | Adds a new element to the end the list | const newList = keyedList.append(list, { id: '5200', author: 'Riley' /* ... */}); | | insert | Insert a new element to the beginning of the list | const newList = keyedList.insert(list, { id: '5200', author: 'Riley' /* ... */}); | | removeById | Removes an element by its id | const newList = keyedList.removeById(list, '2222'); | | map | Iterates through the list elements | const nameArray = keyedList.map(list, p => p.author); | | mapIds | Iterates through the ids of the list | const ids = keyedList.mapIds(list, id => id); | | filter | Filters out elements which stasify a given condition | const nonZeroVotes = keyedList.filter(list, p => p.votes > 0); | | sort | Sorts the array, by a given comparison function | const sortedList = keyedList.sort(list, (left, right) => left.votes - right.votes);|

A more complete example with React

Posts.tsx:

/* ... API handlers ...*/

type PostItemList = keyedList.IdKeyedList<PostItem>;

const Posts = () => {
  const [posts, setPosts] = React.useState<PostItemList>(keyedList.fromArray());
  const [isInitialized, setIsInitialized] = React.useState<boolean>(false);

  React.useEffect(() => {
    if (!isInitialized) {
      if (!initialPosts) {
        fetchAllPosts();
      }
      setIsInitialized(true);
    } else {
      fetchAllPosts();
    }
  }, []);

  const fetchAllPosts = () => {
    // fetchPosts does a GET request to load all posts into an array, then those are converted into a list
    fetchPosts(cs => setPosts(keyedList.fromArray(cs)));
  };

  const submitPost = () => {
    // savePost does the POST request for creating a new post, the new item then can be inserted to the list
    savePost(content, (c) => {
      setPosts(keyedList.insert(posts, c));
      setContent("");
    });
    return true;
  };

  const setPostContent = (id: string, content: string) => {
    // When the content is edited, the new content is updated through this call
    setPosts(keyedList.update(posts, { id, content }));
    return true;
  };

  const voteOnPost = (id: string) => {
    // When the post is voted on, the request will give back the number of all votes, so we can show it to the users
    votePost(id, (votes) => {
      setPosts(keyedList.update(posts, { id, vote: votes }));
    });
  };

  const doDelete = (id: string) => {
    if (confirm('Are you sure to delete this post?')) {
      setPosts(keyedList.removeById(posts, id));
    }
  };

  return (
    <>
      {
        keyedList.mapIds(id =>
          <Post
            id={ id }
            key={ id }
            editContent={ setPostContent }
            getById={ id => keyedList.getById(posts, id) }
            onVote={ id => voteOnPost }
            onDelete={ doDelete } />
        )
      }
    </>
  );
};

export default Posts;

Post.tsx:

interface PostProps {
  id: Uuid;
  onEditContent?: (id: string, content: string) => void;
  onVote?: (id: string) => void;
  onDelete?: (id: string) => void;
  getById?: (id: string) => PostItem;
}


export const Post = ({ id, getById, setContent, onDelete }: PostProps) => {
  const post = (id && getById) ? getById(id) : undefined;

  if (!post)
    return undefined;

  return (
    <div className="post">
      <p><em>Posted by { post.author }</em></p>
      {
        onVote &&
          <button className="vote" onClick={ () => onVote(id) } />
      }
      <p className="post-content">
        { post.content }
      </p>
      {
        onEditContent &&
        /* create and editor for the content and call onEditContent when done */
      }
      {
        onDelete &&
          <button className="delete" onClick={ () => onDelete(id) } />
      }
    </div>
  );
};