Skip to content

Instantly share code, notes, and snippets.

@jkalias
Last active July 21, 2021 22:25
Show Gist options
  • Save jkalias/6accf07af66d38e27b2de1ba8f7552af to your computer and use it in GitHub Desktop.
Save jkalias/6accf07af66d38e27b2de1ba8f7552af to your computer and use it in GitHub Desktop.
(Deprecated, see this repo for more details https://github.com/jkalias/functional_vector) A wrapper for C++ std::vector geared towards a more fluent API, with functional programming in mind. The primary focus is readability at the call site and not performance. Heavily influenced and inspired from C# and Swift.
// MIT License
//
// Copyright (c) 2021 Ioannis Kaliakatsos
//
// 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.
#ifndef functional_vector_h
#define functional_vector_h
#include <vector>
#include <functional>
#include <algorithm>
#include <optional>
template <typename T>
class functional_vector {
public:
functional_vector()
: backing_vector_()
{
}
functional_vector(const std::vector<T> &vector)
: backing_vector_(std::move(vector))
{
}
functional_vector(const std::initializer_list<T>& list)
: backing_vector_(std::move(list))
{
}
functional_vector& add(T value)
{
backing_vector_.push_back(value);
return *this;
}
functional_vector adding(T value) const
{
auto augmented_vector(backing_vector_);
augmented_vector.push_back(value);
return functional_vector(augmented_vector);
}
functional_vector& add_range(const std::vector<T>& vector)
{
backing_vector_.insert(backing_vector_.end(),
vector.begin(),
vector.end());
return *this;
}
functional_vector adding_range(const std::vector<T>& vector) const
{
auto augmented_vector(backing_vector_);
augmented_vector.reserve(augmented_vector.size() + vector.size());
augmented_vector.insert(augmented_vector.end(),
vector.begin(),
vector.end());
return functional_vector(augmented_vector);
}
functional_vector& add_range(const std::initializer_list<T>& list)
{
backing_vector_.insert(backing_vector_.end(),
list.begin(),
list.end());
return *this;
}
functional_vector adding_range(const std::initializer_list<T>& list) const
{
auto augmented_vector(backing_vector_);
augmented_vector.reserve(augmented_vector.size() + list.size());
augmented_vector.insert(augmented_vector.end(),
list.begin(),
list.end());
return functional_vector(augmented_vector);
}
template <typename U>
functional_vector<U> map(const std::function<U(T)>& transform) const
{
std::vector<U> transformed_vector;
transformed_vector.reserve(backing_vector_.size());
std::transform(backing_vector_.begin(),
backing_vector_.end(),
std::back_inserter(transformed_vector),
transform);
return functional_vector<U>(transformed_vector);
}
functional_vector& filter(const std::function<bool(T)>& predicate_to_keep)
{
backing_vector_.erase(std::remove_if(backing_vector_.begin(),
backing_vector_.end(),
[&](const auto& element) { return !predicate_to_keep(element); }),
backing_vector_.end());
return *this;
}
functional_vector filtered(const std::function<bool(T)>& predicate_to_keep) const
{
std::vector<T> filtered_vector;
filtered_vector.reserve(backing_vector_.size());
std::copy_if(backing_vector_.begin(),
backing_vector_.end(),
std::back_inserter(filtered_vector),
predicate_to_keep);
return functional_vector(filtered_vector);
}
functional_vector& reverse()
{
std::reverse(backing_vector_.begin(), backing_vector_.end());
return *this;
}
functional_vector reversed() const
{
std::vector<T> reversed_vector(backing_vector_);
std::reverse(reversed_vector.begin(), reversed_vector.end());
return functional_vector(reversed_vector);
}
template <typename U>
struct functional_vector_tuple {
public:
T first;
U second;
};
template <typename U>
functional_vector<functional_vector_tuple<U>> zip(const std::vector<U>& vector) const
{
assert(backing_vector_.size() == vector.size());
std::vector<functional_vector_tuple<U>> combined_vector;
combined_vector.reserve(vector.size());
for (auto i = 0; i < backing_vector_.size(); i++) {
combined_vector.push_back({ backing_vector_[i], vector[i] });
}
return functional_vector<functional_vector_tuple<U>>(combined_vector);
}
template <typename U>
functional_vector<functional_vector_tuple<U>> zip(const functional_vector<U>& vector) const
{
assert(backing_vector_.size() == vector.size());
std::vector<functional_vector_tuple<U>> combined_vector;
combined_vector.reserve(vector.size());
for (auto i = 0; i < backing_vector_.size(); i++) {
combined_vector.push_back({ backing_vector_[i], vector[i] });
}
return functional_vector<functional_vector_tuple<U>>(combined_vector);
}
functional_vector& sort(const std::function<bool(T, T)>& comparison_predicate)
{
std::sort(backing_vector_.begin(),
backing_vector_.end(),
comparison_predicate);
return *this;
}
functional_vector& sort_ascending()
{
return sort(std::less_equal<T>());
}
functional_vector& sort_descending()
{
return sort(std::greater_equal<T>());
}
functional_vector sorted(const std::function<bool(T, T)>& comparison_predicate) const
{
auto sorted_vector(backing_vector_);
std::sort(sorted_vector.begin(),
sorted_vector.end(),
comparison_predicate);
return functional_vector(sorted_vector);
}
functional_vector sorted_ascending() const
{
return sorted(std::less_equal<T>());
}
functional_vector sorted_descending() const
{
return sorted(std::greater_equal<T>());
}
size_t size() const
{
return backing_vector_.size();
}
T& operator[] (size_t index)
{
assert(index < size());
return backing_vector_[index];
}
const T& operator[] (size_t index) const
{
assert(index < size());
return backing_vector_[index];
}
typename std::vector<T>::iterator begin()
{
return backing_vector_.begin();
}
typename std::vector<T>::const_iterator begin() const
{
return backing_vector_.begin();
}
typename std::vector<T>::iterator end()
{
return backing_vector_.end();
}
typename std::vector<T>::const_iterator end() const
{
return backing_vector_.end();
}
functional_vector& for_each(const std::function<void(T)>& operation)
{
std::for_each(backing_vector_.begin(),
backing_vector_.end(),
operation);
return *this;
}
std::optional<size_t> first_index_of(const T& element) const
{
auto it = std::find(backing_vector_.begin(),
backing_vector_.end(),
element);
if (it != backing_vector_.end()) {
return std::distance(backing_vector_.begin(), it);
}
return std::nullopt;
}
std::optional<size_t> last_index_of(const T& element) const
{
auto it = std::find(backing_vector_.rbegin(),
backing_vector_.rend(),
element);
if (it != backing_vector_.rend()) {
return std::distance(it, backing_vector_.rend()) - 1;
}
return std::nullopt;
}
std::vector<size_t> all_indices_of(const T& element) const
{
std::vector<size_t> indices;
auto it = std::find(backing_vector_.begin(),
backing_vector_.end(),
element);
while (it != backing_vector_.end()) {
indices.push_back(std::distance(backing_vector_.begin(), it));
++it;
it = std::find(it, backing_vector_.end(), element);
}
return indices;
}
functional_vector& remove_at(size_t index)
{
assert(index < size());
backing_vector_.erase(begin() + index);
return *this;
}
functional_vector removing_at(size_t index) const
{
assert(index < size());
auto copy(backing_vector_);
copy.erase(copy.begin() + index);
return functional_vector(copy);
}
functional_vector& remove_last()
{
if (size() == 0) {
return *this;
}
return remove_at(size() - 1);
}
functional_vector removing_last() const
{
if (size() == 0) {
return *this;
}
return removing_at(size() - 1);
}
functional_vector& remove_first()
{
if (size() == 0) {
return *this;
}
return remove_at(0);
}
functional_vector removing_first() const
{
if (size() == 0) {
return *this;
}
return removing_at(0);
}
functional_vector& insert_at(size_t index, const T& element)
{
assert(index < size());
backing_vector_.insert(begin() + index, element);
return *this;
}
functional_vector inserting_at(size_t index, const T& element) const
{
assert(index < size());
auto copy(backing_vector_);
copy.insert(copy.begin() + index, element);
return functional_vector(copy);
}
private:
std::vector<T> backing_vector_;
};
#endif /* functional_vector_h */
// MIT License
//
// Copyright (c) 2021 Ioannis Kaliakatsos
//
// 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.
#include <iostream>
#include <sstream>
#include "functional_vector.h"
std::string convert_to_string(double value);
double convert_to_double(std::string value);
int main(int argc, const char * argv[]) {
// --------------------------------------------------------------------------------------------------------
// a vector of all double values
auto double_values = functional_vector(std::vector { 1.3, 2.5, -2.1 });
// --------------------------------------------------------------------------------------------------------
// mapping all elements of the vector using a lambda
// contains: { "1.3", "2.5", "-2.1" }
auto string_values_using_lambda = double_values.map<std::string>([](const auto& value) -> std::string {
std::ostringstream strs;
strs << value;
auto str = strs.str();
return str;
});
// --------------------------------------------------------------------------------------------------------
// mapping all elements of the vector using a local function
// contains: { "1.3", "2.5", "-2.1" }
auto string_values_using_local_function = double_values.map<std::string>(convert_to_string);
// --------------------------------------------------------------------------------------------------------
// round trip should recover the original double values
// contains: { 1.3, 2.5, -2.1 }
auto double_values_recovered = double_values
.map<std::string>(convert_to_string)
.map<double>(convert_to_double);
// --------------------------------------------------------------------------------------------------------
// create an augmented vector leaves the original untouched
// contains: { 1.3, 2.5, -2.1, 5, -10.3, 1.1, -2, 7 }
auto augmented_double_values = double_values
.adding(5)
.adding(-10.3)
.adding_range({ 1.1, -2, 7});
// --------------------------------------------------------------------------------------------------------
// keep elements which match a predicate
// contains: { 1.3, 2.5, 5, 1.1, 7 }
auto positive_numbers = augmented_double_values.filtered([](const auto& value) { return value >= 0; });
// --------------------------------------------------------------------------------------------------------
// reverse the vector elements
// contains: { 2.5, 5, 7 }
auto larger_than_2 = augmented_double_values.filtered([](const auto& value) { return value >= 2; });
// contains: { 7, 5, 2.5 }
auto larger_than_2_reversed = larger_than_2.reversed();
// --------------------------------------------------------------------------------------------------------
// combine (zip) two vectors for simultaneous processing
auto kid_names = functional_vector<std::string>({ "Mary", "John", "Brian" });
/* contains:
{
(2.5, "Mary"),
(5, "John"),
(7, "Brian")
}
*/
auto kid_ages_and_names = larger_than_2.zip(kid_names);
// --------------------------------------------------------------------------------------------------------
// apply a sorting predicate (returning "true" means the first element should appear before the second one)
// contains: { 1.1, 1.3, 2.5, 5, 7 }
auto positive_numbers_sorted = positive_numbers.sorted([](const auto& n1, const auto& n2) { return n1 < n2; });
// equivalently
// positive_numbers_sorted = positive_numbers.sorted_ascending();
// --------------------------------------------------------------------------------------------------------
// retrieve the size of the vector
// returns 5
auto number_count = positive_numbers_sorted.size();
// --------------------------------------------------------------------------------------------------------
// subscripting and assignment
// contains: { 1.1, 1.3, 1.8, 5, 7 }
positive_numbers_sorted[2] = 1.8;
// --------------------------------------------------------------------------------------------------------
// using iterators to loop through elements
// prints the following
// "Mary is 2.5 years old"
// "John is 5 years old"
// "Brian is 7 years old"
for (const auto& age_and_name: kid_ages_and_names) {
std::cout << age_and_name.second << " is " << age_and_name.first << " years old" << std::endl;
}
// --------------------------------------------------------------------------------------------------------
// index searching operations
auto classroom = functional_vector({ "Jake", "Mike", "John", "Mary", "Tania", "John", "Kate", "Keith", "Jackie" });
if (auto index = classroom.first_index_of("John")) {
// prints "First occurrence of John is at index 2"
std::cout << "First occurrence of John is at index " << index.value() << std::endl;
}
if (!classroom.first_index_of("Jessica").has_value()) {
// prints "Jessica is not found"
std::cout << "Jessica is not found" << std::endl;
}
if (auto index = classroom.last_index_of("John")) {
// prints "Last occurrence of John is at index 5"
std::cout << "Last occurrence of John is at index " << index.value() << std::endl;
}
if (!classroom.last_index_of("Tom").has_value()) {
// prints "Tom is not found"
std::cout << "Tom is not found" << std::endl;
}
// prints the following
// "John is found at index 2"
// "John is found at index 5"
for(const auto& index: classroom.all_indices_of("John")) {
std::cout << "John is found at index " << index << std::endl;
}
// --------------------------------------------------------------------------------------------------------
// execute a function for every element
// prints the following
// "The student's name is Jake"
// "The student's name is Mike"
// "The student's name is John"
// "The student's name is Mary"
// "The student's name is Tania"
// "The student's name is John"
// "The student's name is Kate"
// "The student's name is Keith"
// "The student's name is Jackie"
classroom.for_each([](const auto& student) {
std::cout << "The student's name is " << student << std::endl;
});
// --------------------------------------------------------------------------------------------------------
// remove single elements
// returns { 1.1, 1.8, 5, 7 }
positive_numbers_sorted.removing_at(1);
// returns: { 1.1, 1.3, 1.8, 5 }
positive_numbers_sorted.removing_last();
// --------------------------------------------------------------------------------------------------------
// inserting single elements
// contains: { 1.1, 2.5, 1.3, 1.8, 5, 7 }
positive_numbers_sorted.inserting_at(1, 2.5);
return 0;
}
std::string convert_to_string(double value) {
std::ostringstream strs;
strs << value;
auto str = strs.str();
return str;
}
double convert_to_double(std::string value) {
return atof(value.data());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment