linked-list-0.0.16/0000755000004100000410000000000014123431140014036 5ustar www-datawww-datalinked-list-0.0.16/.travis.yml0000644000004100000410000000007314123431140016147 0ustar www-datawww-datalanguage: ruby rvm: - 2.7 cache: bundler env: CI: true linked-list-0.0.16/test/0000755000004100000410000000000014123431140015015 5ustar www-datawww-datalinked-list-0.0.16/test/conversions_test.rb0000644000004100000410000000147014123431140020753 0ustar www-datawww-datarequire 'test_helper' describe LinkedList::Conversions do describe 'Node()' do it 'returns self if node is given' do node = create_node('foo') assert_equal LinkedList::Conversions.Node(node), node end it 'returns new node' do assert_instance_of LinkedList::Node, LinkedList::Conversions.Node('foo') end end describe 'List()' do it 'returns self if list is given' do list = create_list assert_equal LinkedList::Conversions.List(list), list end it 'returns list with nodes made from array' do list = LinkedList::Conversions.List([1, 2]) assert_equal [1, 2], [list.first, list.last] end it 'returns new list with one node' do list = LinkedList::Conversions.List('foo') assert_equal 'foo', list.first end end end linked-list-0.0.16/test/node_test.rb0000644000004100000410000000134514123431140017331 0ustar www-datawww-datarequire 'test_helper' describe LinkedList::Node do let(:node) { create_node('foo') } describe 'instantiation' do it 'assigns data' do assert_equal 'foo', node.data end it 'assigns nil to next' do assert_nil node.next end it 'assigns nil to prev' do assert_nil node.prev end end describe 'accessors' do it '#data' do node.data = 'bar' assert_equal 'bar', node.data end it '#next' do node.next = 'bar' assert_equal 'bar', node.next end it '#prev' do node.prev = 'xyz' assert_equal 'xyz', node.prev end end describe 'conversion' do it '#to_node returns self' do assert_equal node, node.to_node end end end linked-list-0.0.16/test/list_test.rb0000644000004100000410000004761014123431140017364 0ustar www-datawww-datarequire 'test_helper' describe LinkedList::List do let(:list) { create_list } let(:node_1) { create_node('foo') } let(:node_2) { create_node('bar') } let(:node_3) { create_node('baz') } describe 'instantiation' do it 'assigns first to nil' do assert_nil list.first end it 'assigns last to nil' do assert_nil list.last end it 'has zero length' do assert_equal 0, list.length end end describe '#push' do it 'last pushed node can be accessed with #last' do list.push(node_1) assert_equal node_1.data, list.last end it 'last pushed node data can be accessed with #first' do list.push(node_1) assert_equal node_1.data, list.first end it 'maintains first pushed node as first' do list.push(node_1) list.push(node_2) assert_equal node_1.data, list.first end it 'sets reference to the next node' do list.push(node_1) list.push(node_2) assert_equal node_1.next, node_2 end it 'sets reference to the prev node' do list.push(node_1) list.push(node_2) assert_equal node_2.prev, node_1 end it 'increases list length by 1' do list.push(node_1) assert_equal 1, list.length end it 'returns self' do assert_equal list.push(node_1), list end end describe '#unshift' do it 'last pushed node can be accessed with #last' do list.unshift(node_1) assert_equal node_1.data, list.last end it 'last pushed node can be accessed with #first' do list.unshift(node_1) assert_equal node_1.data, list.first end it 'maintains first pushed node as last' do list.unshift(node_1) list.unshift(node_2) assert_equal node_1.data, list.last end it 'sets reference to the next node' do list.unshift(node_1) list.unshift(node_2) assert_equal node_2.next, node_1 end it 'sets reference to the prev node' do list.unshift(node_1) list.unshift(node_2) assert_equal node_1.prev, node_2 end it 'increases list length by 1' do list.unshift(node_1) assert_equal 1, list.length end it 'returns self' do assert_equal list.unshift(node_1), list end end describe '#pop' do it 'returns nil if list is empty' do assert_nil list.pop end it 'returns node from the end of the list' do list.push(node_1) list.push(node_2) assert_equal node_2.data, list.pop end it 'sets #last to nil when all nodes are removed' do list.push(node_1) list.pop assert_nil list.last end it 'sets #first to nil when all nodes are removed' do list.push(node_1) list.pop assert_nil list.first end it 'reduces list length by 1' do list.push(node_1) list.pop assert_equal 0, list.length end it 'maintains list when multiple nodes are removed' do list.push(node_1) list.push(node_2) list.pop assert_equal [node_1.data], list.to_a end end describe '#insert' do it 'raises error if after and before are passed' do err = assert_raises ArgumentError do assert_nil list.insert(1, after: 'x', before: 'y') end assert_equal err.message, 'either :after or :before keys should be passed' end it 'raises error if no after nore before are passed' do err = assert_raises ArgumentError do assert_nil list.insert(1) end assert_equal err.message, 'either :after or :before keys should be passed' end describe 'after:' do describe 'by block' do it 'does not add value if insert after not found' do list.push('foo') assert_nil list.insert(1, after: ->(d) { d == 'foo1' }) assert_equal ['foo'], list.to_a end it 'inserts value after first matching node by block' do list.push('foo') list.push('bar') assert_equal 1, list.insert(1, after: ->(d) { d == 'foo' }) assert_equal ['foo', 1, 'bar'], list.to_a assert_equal 3, list.length end describe 'position edge cases' do before do list.push(0) list.push(1) list.push(2) end it 'inserts after in the middle' do list.insert('foo', after: ->(d) { d == 0 }) assert_equal [0, 'foo', 1, 2], list.to_a end it 'inserts after the tail' do list.insert('foo', after: ->(d) { d == 2 }) assert_equal [0, 1, 2, 'foo'], list.to_a assert_equal 'foo', list.last end end end describe 'by value' do it 'does not add value if insert after not found' do list.push('foo') assert_nil list.insert(1, after: 'foo1') assert_equal ['foo'], list.to_a end it 'inserts value after first matching node by block' do list.push('foo') list.push('bar') assert_equal 1, list.insert(1, after: 'foo') assert_equal ['foo', 1, 'bar'], list.to_a assert_equal 3, list.length end describe 'position edge cases' do before do list.push(0) list.push(1) list.push(2) end it 'inserts after in the middle' do list.insert('foo', after: 0) assert_equal [0, 'foo', 1, 2], list.to_a end it 'inserts after the tail' do list.insert('foo', after: 2) assert_equal [0, 1, 2, 'foo'], list.to_a assert_equal 'foo', list.last end end end end describe ':before' do describe 'by block' do it 'does not add value if insert before not found' do list.push('foo') assert_nil list.insert(1, before: ->(d) { d == 'foo1' }) assert_equal ['foo'], list.to_a end it 'inserts value before first matching node by block' do list.push('foo') list.push('bar') assert_equal 1, list.insert(1, before: ->(d) { d == 'foo' }) assert_equal [1, 'foo', 'bar'], list.to_a assert_equal 3, list.length end describe 'position edge cases' do before do list.push(0) list.push(1) list.push(2) end it 'inserts before head' do list.insert('foo', before: ->(d) { d == 0 }) assert_equal ['foo', 0, 1, 2], list.to_a assert_equal 'foo', list.first end it 'inserts before in the middle' do list.insert('foo', before: ->(d) { d == 2 }) assert_equal [0, 1, 'foo', 2], list.to_a end end end describe 'by value' do it 'does not add value if insert before not found' do list.push('foo') assert_nil list.insert(1, before: 'foo1') assert_equal ['foo'], list.to_a end it 'inserts value before first' do list.push('foo') list.push('bar') assert_equal 1, list.insert(1, before: 'foo') assert_equal [1, 'foo', 'bar'], list.to_a assert_equal 3, list.length end describe 'position edge cases' do before do list.push(0) list.push(1) list.push(2) end it 'inserts before head' do list.insert('foo', before: 0) assert_equal ['foo', 0, 1, 2], list.to_a assert_equal 'foo', list.first end it 'inserts before in the middle' do list.insert('foo', before: 2) assert_equal [0, 1, 'foo', 2], list.to_a end end end end end describe '#insert_after_node' do it 'inserts value after passed node' do list.push('foo') list.push('bar') node = list.each_node.find { |n| n.data == 'foo' } assert_equal 1, list.insert_after_node(1, node).data assert_equal ['foo', 1, 'bar'], list.to_a assert_equal 3, list.length end describe 'position edge cases' do before do list.push(0) list.push(1) list.push(2) end it 'inserts after in the middle' do node = list.each_node.find { |n| n.data == 0 } list.insert_after_node('foo', node) assert_equal [0, 'foo', 1, 2], list.to_a end it 'inserts after the tail' do node = list.each_node.find { |n| n.data == 2 } list.insert_after_node('foo', node) assert_equal [0, 1, 2, 'foo'], list.to_a assert_equal 'foo', list.last end end end describe '#insert_after_node' do it 'inserts value before first' do list.push('foo') list.push('bar') node = list.each_node.find { |n| n.data == 'foo' } assert_equal 1, list.insert_before_node(1, node).data assert_equal [1, 'foo', 'bar'], list.to_a assert_equal 3, list.length end describe 'position edge cases' do before do list.push(0) list.push(1) list.push(2) end it 'inserts before head' do node = list.each_node.find { |n| n.data == 0 } list.insert_before_node('foo', node) assert_equal ['foo', 0, 1, 2], list.to_a assert_equal 'foo', list.first end it 'inserts before in the middle' do node = list.each_node.find { |n| n.data == 2 } list.insert_before_node('foo', node) assert_equal [0, 1, 'foo', 2], list.to_a end end end describe '#delete' do it 'raises error if block and value are passed' do err = assert_raises ArgumentError do assert_nil list.delete('x') { |d| d == 'x' } end assert_equal err.message, 'either value or block should be passed' end describe 'by block' do it 'returns nil if list is empty' do assert_nil list.delete { |d| d == 'x' } end it 'deletes value in first matching node' do calls_count = 0 list.push('foo') list.push('foo') list.push('bar') list.push('foo') list.delete { |d| calls_count += 1;d == 'bar' } assert_equal ['foo', 'foo', 'foo'], list.to_a assert_equal 3, calls_count end it 'returns deleted value' do list.push('foo') assert_equal 'foo', list.delete { |d| d == 'foo' } end it 'decreases length of list' do list.push('foo') list.push('bar') list.push('foo') list.delete { |d| d == 'foo' } assert_equal 2, list.length assert_equal ['bar', 'foo'], list.to_a end describe 'position edge cases' do before do list.push(0) list.push(1) list.push(2) end it 'deletes value from head' do list.delete { |d| d == 0 } assert_equal [1, 2], list.to_a assert_equal 1, list.first end it 'deletes value from middle' do list.delete { |d| d == 1 } assert_equal [0, 2], list.to_a end it 'deletes value from tail' do list.delete { |d| d == 2 } assert_equal [0, 1], list.to_a assert_equal 1, list.last end end end describe 'by data equality' do it 'returns nil if list is empty' do assert_nil list.delete('x') end it 'deletes value in first node' do list.push('foo') list.push('foo') list.push('bar') list.push('foo') list.delete('foo') assert_equal ['foo', 'bar', 'foo'], list.to_a end it 'returns deleted value' do list.push('foo') assert_equal 'foo', list.delete('foo') end it 'decreases length of list' do list.push('foo') list.push('bar') list.push('foo') list.delete('foo') assert_equal 2, list.length assert_equal ['bar', 'foo'], list.to_a end describe 'position edge cases' do before do list.push(0) list.push(1) list.push(2) end it 'deletes value from head' do list.delete(0) assert_equal [1, 2], list.to_a assert_equal 1, list.first end it 'deletes value from middle' do list.delete(1) assert_equal [0, 2], list.to_a end it 'deletes value from tail' do list.delete(2) assert_equal [0, 1], list.to_a assert_equal 1, list.last end end end describe 'by node' do it 'deletes first node' do list.push(node_1) list.push(node_2) list.push(node_3) list.delete(node_1) assert_equal ['bar', 'baz'], list.to_a end it 'returns deleted value' do list.push(node_1) list.delete(node_1) assert_equal node_1.data, list.delete(node_1) end it 'decreases length of list' do list.push('foo') list.push('bar') list.push('baz') list.delete('foo') assert_equal 2, list.length end describe 'position edge cases' do before do list.push(node_1) list.push(node_2) list.push(node_3) end it 'deletes value from head' do list.delete(node_1) assert_equal [node_2.data, node_3.data], list.to_a assert_equal node_2.data, list.first assert_equal node_3.data, list.last end it 'deletes value from middle' do list.delete(node_2) assert_equal [node_1.data, node_3.data], list.to_a assert_equal node_1.data, list.first assert_equal node_3.data, list.last end it 'deletes value from tail' do list.delete(node_3) assert_equal [node_1.data, node_2.data], list.to_a assert_equal node_1.data, list.first assert_equal node_2.data, list.last end end describe 'delete edge cases' do it 'resets original list state when deleting the last node of the list' do assert_nil list.first assert_nil list.last list.push(node_1) assert_equal node_1.data, list.first assert_equal node_1.data, list.last list.delete(node_1) assert_nil list.first assert_nil list.last end end end end describe '#delete_all' do it 'raises error if block and value are passed' do err = assert_raises ArgumentError do assert_nil list.delete_all('x') { |d| d == 'x' } end assert_equal err.message, 'either value or block should be passed' end describe 'by block' do it 'returns nil if list is empty' do assert_equal list.delete_all { |d| d == 'x' }, [] end it 'deletes value in first matching node' do list.push('foo') list.push('foo') list.push('bar') list.push('foo') list.delete_all { |d| d == 'foo' } assert_equal ['bar'], list.to_a end it 'returns deleted value' do list.push('foo') assert_equal ['foo'], list.delete_all { |d| d == 'foo' } end it 'decreases length of list' do list.push('foo') list.push('bar') list.push('foo') list.delete_all { |d| d == 'foo' } assert_equal 1, list.length assert_equal ['bar'], list.to_a end end describe 'by data equality' do it 'returns nil if list is empty' do assert_nil list.delete('x') end it 'deletes all matched values' do list.push('foo') list.push('foo') list.push('bar') list.push('foo') list.delete_all('foo') assert_equal ['bar'], list.to_a end it 'returns deleted value' do list.push('foo') assert_equal ['foo'], list.delete_all('foo') end it 'decreases length of list' do list.push('foo') list.push('bar') list.push('foo') list.delete_all('foo') assert_equal 1, list.length assert_equal ['bar'], list.to_a end end end describe '#shift' do it 'returns nil if list is empty' do assert_nil list.shift end it 'returns node from the top of the list' do list.push(node_1) list.push(node_2) assert_equal node_1.data, list.shift end it 'sets #last to nil when all nodes are removed' do list.push(node_1) list.shift assert_nil list.last end it 'sets #first to nil when all nodes are removed' do list.push(node_1) list.shift assert_nil list.first end it 'sets reference to the prev node' do list.push(node_1) list.push(node_2) assert_nil node_1.prev assert_equal node_2.prev, node_1 list.shift assert_nil node_2.prev end it 'reduces list length by 1' do list.push(node_1) list.shift assert_equal 0, list.length end end describe '#reverse' do it 'returns new empty list when receiver list is empty' do refute_equal list, list.reverse end it 'returns new list in reverse order' do list.push(node_1) refute_equal list.reverse, list.reverse! end it 'reverses order of nodes' do list.push(node_1) list.push(node_2) new_list = list.reverse assert_equal %w(bar foo), [new_list.first, new_list.last] end end describe '#reverse!' do it 'returns self when list is empty' do assert_equal list, list.reverse! end it 'reverses order of nodes' do list.push(node_1) list.push(node_2) list.reverse! assert_equal [node_2.data, node_1.data], [list.first, list.last] end it 'returns same object' do list.push(node_1) assert_equal list, list.reverse! end end describe '#each_node' do it 'returns enumerator if no block given' do assert_instance_of Enumerator, list.each_node end it 'pass each node data to the block' do list.push(node_1) list.push(node_2) nodes = [] list.each_node { |e| nodes << e } assert_equal %w(foo bar), nodes.map(&:data) assert_equal true, nodes.all? { |n| n.is_a?(LinkedList::Node) } end end describe '#each' do it 'returns enumerator if no block given' do assert_instance_of Enumerator, list.each end it 'pass each node data to the block' do list.push(node_1) list.push(node_2) nodes = [] list.each { |e| nodes << e } assert_equal %w(foo bar), nodes end end describe '#each_node' do it 'returns enumerator if no block given' do assert_instance_of Enumerator, list.each end it 'pass each node data to the block' do list.push(node_1) list.push(node_2) nodes = [] list.each_node { |e| nodes << e } assert_equal %w(foo bar), nodes.map(&:data) end end describe '#reverse_each' do it 'returns enumerator if no block given' do assert_instance_of Enumerator, list.each end it 'pass each node data to the block' do list.push(node_1) list.push(node_2) nodes = [] list.reverse_each { |e| nodes << e } assert_equal %w(bar foo), nodes end end describe '#reverse_each_node' do it 'returns enumerator if no block given' do assert_instance_of Enumerator, list.each end it 'pass each node data to the block' do list.push(node_1) list.push(node_2) nodes = [] list.reverse_each_node { |e| nodes << e } assert_equal %w(bar foo), nodes.map(&:data) end end describe '#inspect' do it 'includes class name' do assert_match(/LinkedList::List/, list.inspect) end it 'includes object id' do assert_match(/#{list.object_id.to_s(16)}/, list.inspect) end it 'includes node values' do list.push(node_1) assert_match(/foo/, list.inspect) end end describe 'conversion' do it '#to_list returns self' do assert_equal list, list.to_list end end end linked-list-0.0.16/test/test_helper.rb0000644000004100000410000000045514123431140017664 0ustar www-datawww-dataif ENV['CI'] require 'coveralls' Coveralls.wear! end require 'linked-list' gem 'minitest' require 'minitest/autorun' require 'minitest/pride' class Minitest::Spec def create_node(*args) LinkedList::Node.new(*args) end def create_list(*args) LinkedList::List.new(*args) end end linked-list-0.0.16/README.md0000644000004100000410000000475414123431140015327 0ustar www-datawww-data[![Code Climate](https://codeclimate.com/github/spectator/linked-list.png)](https://codeclimate.com/github/spectator/linked-list) [![Build Status](https://secure.travis-ci.org/spectator/linked-list.png?branch=master)](http://travis-ci.org/spectator/linked-list) [![Gem Version](https://badge.fury.io/rb/linked-list.png)](http://badge.fury.io/rb/linked-list) [![Coverage Status](https://coveralls.io/repos/spectator/linked-list/badge.png)](https://coveralls.io/r/spectator/linked-list) # LinkedList Ruby implementation of Doubly Linked List, following some Ruby idioms. ## Installation Add this line to your application's Gemfile: ```ruby gem 'linked-list' ``` And then execute: ```shell $ bundle ``` Or install it yourself as: ```shell $ gem install linked-list ``` ## Usage ```ruby object = Object.new # could be anything list = LinkedList::List.new list.push(object) list << object # same as `push` list.unshift(object) list.pop list.shift list.insert(object, before: object) list.insert(object, before: ->(n) { n == 'foo' }) list.insert(object, after: object) list.insert(object, after: ->(n) { n == 'foo' }) list.insert_before(object, node) list.insert_after(object, node) list.reverse list.reverse! list.delete(object) list.delete { |n| n == 'foo' } list.delete_all(object) list.delete_all { |n| n == 'foo' } list.each # Enumerator object list.each { |e| puts e } list.reverse_each # Enumerator object list.reverse_each { |e| puts e } list.reverse_each_node # Enumerator object list.reverse_each_node { |node| puts node.data } list.first # head of the list list.last # tail of the list list.length list.size # same as `length` list.to_a ``` Another way to instantiate `List` or `Node` is to use conversion functions. First, include `LinkedList::Conversions` module to your class ```ruby class Foo include LinkedList::Conversions end ``` Now anywhere in your class you can use the following methods ```ruby Node(object) # will return new `Node` object List(object) # will return new `List` object with one `Node` object List([object, object]) # will return new `List` object with two `Node` objects ``` Please see `LinkedList::List`, `LinkedList::Node`, and `LinkedList::Conversions` for details. ## Tests Run test with ```shell $ rake ``` ## Contributing 1. Fork it 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your changes (`git commit -am 'Add some feature'`) 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request linked-list-0.0.16/bin/0000755000004100000410000000000014123431140014606 5ustar www-datawww-datalinked-list-0.0.16/bin/console0000755000004100000410000000020414123431140016172 0ustar www-datawww-data#!/usr/bin/env ruby # frozen_string_literal: true require 'bundler/setup' require 'linked-list' require 'irb' IRB.start(__FILE__) linked-list-0.0.16/bin/setup0000755000004100000410000000011214123431140015666 0ustar www-datawww-data#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -vx bundle install linked-list-0.0.16/CHANGELOG.md0000644000004100000410000000376614123431140015663 0ustar www-datawww-data# 0.0.x / Unreleased # 0.0.16 / 2021-09-21 ## Fixed - Fixed bug when reference to prev node was not set correctly аfter using `shift`. [Compare v0.0.15...v0.0.16](https://github.com/spectator/linked-list/compare/v0.0.15...v0.0.16) # 0.0.15 / 2020-05-26 ## Fixed - Fixed bug when deleting the last node of the list. The @tail value was not updated if the @head value was updated. [Compare v0.0.14...v0.0.15](https://github.com/spectator/linked-list/compare/v0.0.14...v0.0.15) # 0.0.14 / 2020-04-17 ## Fixed Forced minimal rake version in gemspec to skip rake versions with security issues. [Compare v0.0.13...v0.0.14](https://github.com/spectator/linked-list/compare/v0.0.13...v0.0.14) # 0.0.13 / 2018-11-27 ## Fixed - Delete duplicate method definitions (nex3 in [#7](https://github.com/spectator/linked-list/pull/7)) ## Added - Allow `List#delete` to delete a `Node` in O(1) time (nex3 in [#6](https://github.com/spectator/linked-list/pull/6)) [Compare v0.0.12...v0.0.13](https://github.com/spectator/linked-list/compare/v0.0.12...v0.0.13) # 0.0.12 / 2018-09-04 ## Added - Added `insert`, `insert_before` and `insert_after` methods (mpospelov in [#3](https://github.com/spectator/linked-list/pull/3)) - Added `reverse_each` and `reverse_each_node` methods (mpospelov in [#4](https://github.com/spectator/linked-list/pull/4)) [Compare v0.0.11...v0.0.12](https://github.com/spectator/linked-list/compare/v0.0.11...v0.0.12) # 0.0.11 / 2018-08-23 ## Added - Added `delete` and `delete_all` methods (mpospelov in [#2](https://github.com/spectator/linked-list/pull/2)) [Compare v0.0.10...v0.0.11](https://github.com/spectator/linked-list/compare/v0.0.10...v0.0.11) # 0.0.10 / 2018-04-02 ## Fixed - Fixed bug when `@tail.prev` was mistekenly set to `nil` instead of `@tail.next` when popping elements off the list (Sonna in [#1](https://github.com/spectator/linked-list/pull/1)) [Compare v0.0.9...v0.0.10](https://github.com/spectator/linked-list/compare/v0.0.9...v0.0.10) # 0.0.9 / 2013-12-11 Initial release. linked-list-0.0.16/.gitignore0000644000004100000410000000023214123431140016023 0ustar www-datawww-data*.gem *.rbc .bundle .config .yardoc Gemfile.lock InstalledFiles _yardoc coverage doc/ lib/bundler/man pkg rdoc spec/reports test/tmp test/version_tmp tmp linked-list-0.0.16/Rakefile0000644000004100000410000000023614123431140015504 0ustar www-datawww-datarequire "bundler/gem_tasks" require 'rake/testtask' Rake::TestTask.new do |t| t.libs << 'test' t.pattern = 'test/*_test.rb' end task default: 'test' linked-list-0.0.16/lib/0000755000004100000410000000000014123431140014604 5ustar www-datawww-datalinked-list-0.0.16/lib/linked-list/0000755000004100000410000000000014123431140017023 5ustar www-datawww-datalinked-list-0.0.16/lib/linked-list/list.rb0000644000004100000410000001703514123431140020331 0ustar www-datawww-data# frozen_string_literal: true module LinkedList class List include Conversions attr_reader :length alias_method :size, :length def initialize @head = nil @tail = nil @length = 0 end # Returns the first element of the list or nil. # def first @head && @head.data end # Returns the last element of the list or nil. # def last @tail && @tail.data end # Pushes new nodes to the end of the list. # # == Parameters: # node:: Any object, including +Node+ objects. # # == Returns: # +self+ of +List+ object. # def push(node) node = Node(node) @head ||= node if @tail @tail.next = node node.prev = @tail end @tail = node @length += 1 self end alias_method :<<, :push # Pushes new nodes on top of the list. # # == Parameters: # node:: Any object, including +Node+ objects. # # == Returns: # +self+ of +List+ object. # def unshift(node) node = Node(node) @tail ||= node node.next = @head @head.prev = node if @head @head = node @length += 1 self end # Inserts after or before first matched node.data from the the list by passed block or value. # # == Returns: # Inserted node data # def insert(to_add, after: nil, before: nil) if after && before || !after && !before raise ArgumentError, 'either :after or :before keys should be passed' end matcher = after || before matcher_proc = if matcher.is_a?(Proc) __to_matcher(&matcher) else __to_matcher(matcher) end node = each_node.find(&matcher_proc) return unless node new_node = after ? insert_after_node(to_add, node) : insert_before_node(to_add, node) new_node.data end # Inserts data after first matched node.data. # # == Returns: # Inserted node # def insert_after_node(data, node) Node(data).tap do |new_node| new_node.prev = node new_node.next = node.next if node.next node.next.prev = new_node else @tail = new_node end node.next = new_node @length += 1 end end # Inserts data before first matched node.data. # # == Returns: # Inserted node # def insert_before_node(data, node) Node(data).tap do |new_node| new_node.next = node new_node.prev = node.prev if node.prev node.prev.next = new_node else @head = new_node end node.prev = new_node @length += 1 end end # Removes first matched node.data from the the list by passed block or value. # # If +val+ is a +Node+, removes that node from the list. Behavior is # undefined if +val+ is a +Node+ that's not a member of this list. # # == Returns: # Deleted node's data # def delete(val = nil, &block) if val.respond_to?(:to_node) node = val.to_node __unlink_node(node) return node.data end each_node.find(&__to_matcher(val, &block)).tap do |node_to_delete| return unless node_to_delete __unlink_node(node_to_delete) end.data end # Removes all matched data.data from the the list by passed block or value. # # == Returns: # Array of deleted nodes # def delete_all(val = nil, &block) each_node.select(&__to_matcher(val, &block)).each do |node_to_delete| next unless node_to_delete __unlink_node(node_to_delete) end.map(&:data) end # Removes data from the end of the list. # # == Returns: # Data stored in the node or nil. # def pop return nil unless @head tail = __pop @head = nil unless @tail @length -= 1 tail.data end # Removes data from the beginning of the list. # # == Returns: # Data stored in the node or nil. # def shift return nil unless @head head = __shift @tail = nil unless @head @length -= 1 head.data end # Reverse list of nodes and returns new instance of the list. # # == Returns: # New +List+ in reverse order. # def reverse List(to_a).reverse! end # Reverses list of nodes in place. # # == Returns: # +self+ in reverse order. # def reverse! return self unless @head __each do |curr_node| curr_node.prev, curr_node.next = curr_node.next, curr_node.prev end @head, @tail = @tail, @head self end # Iterates over nodes from top to bottom passing node data to the block if # given. If no block given, returns +Enumerator+. # # == Returns: # +Enumerator+ or yields data to the block stored in every node on the # list. # def each return to_enum(__callee__) unless block_given? __each { |node| yield(node.data) } end # Iterates over nodes from top to bottom passing node(LinkedList::Node instance) # to the block if given. If no block given, returns +Enumerator+. # # == Returns: # +Enumerator+ or yields list nodes to the block # def each_node return to_enum(__callee__) unless block_given? __each { |node| yield(node) } end # Iterates over nodes from bottom to top passing node data to the block if # given. If no block given, returns +Enumerator+. # # == Returns: # +Enumerator+ or yields data to the block stored in every node on the # list. # def reverse_each return to_enum(__callee__) unless block_given? __reverse_each { |node| yield(node.data) } end # Iterates over nodes from bottom to top passing node(LinkedList::Node instance) # to the block if given. If no block given, returns +Enumerator+. # # == Returns: # +Enumerator+ or yields list nodes to the block # def reverse_each_node return to_enum(__callee__) unless block_given? __reverse_each { |node| yield(node) } end # Converts list to array. # def to_a each.to_a end alias_method :to_ary, :to_a def inspect sprintf('#<%s:%#x %s>', self.class, self.__id__, to_a.inspect) end # Conversion function, see +Conversions.List+. # # == Returns: # +self+ # def to_list self end private def __unlink_node(node) @head = node.next if node.prev.nil? @tail = node.prev if node.next.nil? if node.prev.nil? node.next.prev = nil if node.next elsif node.next.nil? node.prev.next = nil if node.prev else node.prev.next, node.next.prev = node.next, node.prev end @length -= 1 end def __to_matcher(val = nil, &block) raise ArgumentError, 'either value or block should be passed' if val && block_given? block = ->(e) { e == val } unless block_given? ->(node) { block.call(node.data) } end def __shift head = @head @head = @head.next @head.prev = nil if @head head end def __pop tail = @tail @tail = @tail.prev @tail.next = nil if @tail tail end def __reverse_each curr_node = @tail while(curr_node) yield curr_node curr_node = curr_node.prev end end def __each curr_node = @head while(curr_node) yield curr_node curr_node = curr_node.next end end end end linked-list-0.0.16/lib/linked-list/version.rb0000644000004100000410000000013414123431140021033 0ustar www-datawww-data# frozen_string_literal: true module Linked module List VERSION = '0.0.16' end end linked-list-0.0.16/lib/linked-list/conversions.rb0000644000004100000410000000220114123431140021713 0ustar www-datawww-data# frozen_string_literal: true module LinkedList module Conversions module_function # +Node()+ tries to convert its argument to +Node+ object by first calling # +#to_node+, if that is not availabe then it will instantiate a new +Node+ # object. # # == Returns: # New +Node+ object. # def Node(arg) if arg.respond_to?(:to_node) arg.to_node else Node.new(arg) end end # +List()+ tries to conver its argument to +List+ object by first calling # +#to_list+, if that is not availabe and its argument is an array (or can # be convertd into array with +#to_ary+) then it will instantiate a new # +List+ object making nodes from array elements. If none above applies, # then a new +List+ will be instantiated with one node holding argument # value. # # == Returns: # New +List+ object. # def List(arg) if arg.respond_to?(:to_list) arg.to_list elsif arg.respond_to?(:to_ary) arg.to_ary.each_with_object(List.new) { |n, l| l.push(Node(n)) } else List.new.push(Node(arg)) end end end end linked-list-0.0.16/lib/linked-list/node.rb0000644000004100000410000000050714123431140020277 0ustar www-datawww-data# frozen_string_literal: true module LinkedList class Node attr_accessor :data, :next, :prev def initialize(data) @data = data @next = nil @prev = nil end # Conversion function, see +Conversions.Node+. # # == Returns: # self # def to_node self end end end linked-list-0.0.16/lib/linked-list.rb0000644000004100000410000000016714123431140017354 0ustar www-datawww-data# frozen_string_literal: true require 'linked-list/conversions' require 'linked-list/node' require 'linked-list/list' linked-list-0.0.16/linked-list.gemspec0000644000004100000410000000222114123431140017617 0ustar www-datawww-data# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'linked-list/version' Gem::Specification.new do |spec| spec.name = 'linked-list' spec.version = Linked::List::VERSION spec.authors = ['Yury Velikanau'] spec.email = ['yury.velikanau@gmail.com'] spec.description = %q(Ruby implementation of Doubly Linked List, following some Ruby idioms.) spec.summary = %q(Ruby implementation of Doubly Linked List, following some Ruby idioms.) spec.homepage = 'https://github.com/spectator/linked-list' spec.license = 'MIT' spec.files = `git ls-files`.split($/) spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] spec.add_development_dependency 'bundler', '>= 2.0', '< 3.0' spec.add_development_dependency 'coveralls', '~> 0' spec.add_development_dependency 'm', '~> 1.5', '>= 1.5.0' spec.add_development_dependency 'minitest', '>= 5.0', '<= 6.0' spec.add_development_dependency 'rake', '>= 12.3.3' end linked-list-0.0.16/Gemfile0000644000004100000410000000014014123431140015324 0ustar www-datawww-datasource 'https://rubygems.org' # Specify your gem's dependencies in linked-list.gemspec gemspec linked-list-0.0.16/LICENSE.txt0000644000004100000410000000205714123431140015665 0ustar www-datawww-dataCopyright (c) 2013 Yury Velikanau MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.