Simpler way to set multiple array slots to one value

13,289

Solution 1

This function will help make it less painful.

void initialize(int * arr, std::initializer_list<std::size_t> list, int value) {
    for (auto i : list) {
        arr[i] = value;
    }
}

Call it like this.

initialize(array,{9,5,14},2);

Solution 2

A variant of aaronman's answer:

template <typename T>
void initialize(T array[], const T& value)
{
}

template <size_t index, size_t... indices, typename T>
void initialize(T array[], const T& value)
{
    array[index] = value;
    initialize<indices...>(array, value);
}

int main()
{
    int array[10];

    initialize<0,3,6>(array, 99);

    std::cout << array[0] << " " << array[3] << " " << array[6] << std::endl;
}

Example: Click here

Solution 3

Just for the fun of it I created a somewhat different approach which needs a bit of infrastructure allowing initialization like so:

double array[40] = {};
"9 5 14"_idx(array) = 1;
"8 15 23 12"_idx(array) = 2;

If the digits need to be separated by commas, there is a small change needed. In any case, here is the complete code:

#include <algorithm>
#include <iostream>
#include <sstream>
#include <iterator>

template <int Size, typename T = int>
class assign
{
    int  d_indices[Size];
    int* d_end;
    T*   d_array;
    void operator=(assign const&) = delete;
public:
    assign(char const* base, std::size_t n)
        : d_end(std::copy(std::istream_iterator<int>(
                      std::istringstream(std::string(base, n)) >> std::skipws),
                          std::istream_iterator<int>(), this->d_indices))
        , d_array()
    {
    }
    assign(assign<Size>* as, T* a)
        : d_end(std::copy(as->begin(), as->end(), this->d_indices))
        , d_array(a) {
    }
    assign(assign const& o)
        : d_end(std::copy(o.begin(), o.end(), this->d_indices))
        , d_array(o.d_array)
    {
    }
    int const* begin() const { return this->d_indices; }
    int const* end() const   { return this->d_end; }
    template <typename A>
    assign<Size, A> operator()(A* array) {
        return assign<Size, A>(this, array);
    }
    void operator=(T const& value) {
        for (auto it(this->begin()), end(this->end()); it != end; ++it) {
            d_array[*it] = value;
        }
    }
};

assign<30> operator""_idx(char const* base, std::size_t n)
{
    return assign<30>(base, n);
}

int main()
{
    double array[40] = {};
    "1 3 5"_idx(array) = 17;
    "4 18 7"_idx(array) = 19;
    std::copy(std::begin(array), std::end(array),
              std::ostream_iterator<double>(std::cout, " "));
    std::cout << "\n";
}

Solution 4

I just had a play around for the sake of fun / experimentation (Note my concerns at the bottom of the answer):

It's used like this:

smartAssign(array)[0][8]       = 1;
smartAssign(array)[1][4][2]    = 2;
smartAssign(array)[3]          = 3;
smartAssign(array)[5][9][6][7] = 4;

Source code:

#include <assert.h> //Needed to test variables
#include <iostream>
#include <cstddef>

template <class ArrayPtr, class Value>
class SmartAssign
{
    ArrayPtr m_array;

public:
    class Proxy
    {
        ArrayPtr m_array;
        size_t m_index;
        Proxy* m_prev;

        Proxy(ArrayPtr array, size_t index)
            : m_array(array)
            , m_index(index)
            , m_prev(nullptr)
        { }

        Proxy(Proxy* prev, size_t index)
            : m_array(prev->m_array)
            , m_index(index)
            , m_prev(prev)
        { }

        void assign(Value value)
        {
            m_array[m_index] = value;            
            for (auto prev = m_prev; prev; prev = prev->m_prev) {
                m_array[prev->m_index] = value;
            }
        }

    public:
        void operator=(Value value)
        {
            assign(value);
        }

        Proxy operator[](size_t index)
        {
          return Proxy{this, index};
        }

        friend class SmartAssign;
    };

    SmartAssign(ArrayPtr array)
        : m_array(array)
    {
    }


    Proxy operator[](size_t index)
    {
        return Proxy{m_array, index};
    }
};

template <class T>
SmartAssign<T*, T> smartAssign(T* array)
{
    return SmartAssign<T*, T>(array);
}

int main()
{
    int array[10];

    smartAssign(array)[0][8]       = 1;
    smartAssign(array)[1][4][2]    = 2;
    smartAssign(array)[3]          = 3;
    smartAssign(array)[5][9][6][7] = 4;

    for (auto i : array) {
        std::cout << i << "\n";
    }

    //Now to test the variables
    assert(array[0] == 1 && array[8] == 1);
    assert(array[1] == 2 && array[4] == 2 && array[2] == 2);
    assert(array[3] == 3);
    assert(array[5] == 4 && array[9] == 4 && array[6] == 4 && array[7] == 4);
}

Let me know what you think, I don't typically write much code like this, I'm sure someone will point out some problems somewhere ;)

I'm not a 100% certain of the lifetime of the proxy objects.

Solution 5

The best you can do if your indexes are unrelated is "chaining" the assignments:

array[9] = array[5] = array[14] = 1;

However if you have some way to compute your indexes in a deterministic way you could use a loop:

for (size_t i = 0; i < 3; ++i)
    array[transform_into_index(i)] = 1;

This last example also obviously applies if you have some container where your indexes are stored. So you could well do something like this:

const std::vector<size_t> indexes = { 9, 5, 14 };
for (auto i: indexes)
    array[i] = 1;
Share:
13,289

Related videos on Youtube

Matthew D. Scholefield
Author by

Matthew D. Scholefield

I'm a passionate open source developer. Whenever I find something I don't understand, I rebuild it. Aside from programming, I occasionally do 3D graphics (With Blender), and a little music composition.

Updated on June 14, 2022

Comments

  • Matthew D. Scholefield
    Matthew D. Scholefield almost 2 years

    I'm coding in C++, and I have the following code:

    int array[30];
    array[9] = 1;
    array[5] = 1;
    array[14] = 1;
    
    array[8] = 2;
    array[15] = 2;
    array[23] = 2;
    array[12] = 2;
    //...
    

    Is there a way to initialize the array similar to the following?

    int array[30];
    array[9,5,14] = 1;
    array[8,15,23,12] = 2;
    //...
    

    Note: In the actual code, there can be up to 30 slots that need to be set to one value.

    • aaronman
      aaronman over 10 years
      if they aren't contiguous it doesn't get much better than this, you could write a function that takes the array and the indicies
    • chris
      chris over 10 years
      There's a GCC extension for this I think. Other than that, there's always array[9] = array[5] = array[14] = 1; I guess.
    • RamonBoza
      RamonBoza over 10 years
      you can also do int number[3] = { 5, 7, 2 };
  • Tommy
    Tommy over 10 years
    With only 30 values, I'm not sure if the overhead of calling a function transform_into_index() is worth a few less lines of code. Performance hit for nothing.
  • aschepler
    aschepler over 10 years
    Was about to post pretty much the same thing (but with templates).
  • aschepler
    aschepler over 10 years
    In C++, a function call is often not a performance hit at all. Also, premature optimization is the root of all evil.
  • syam
    syam over 10 years
    @Tommy I for one am not sure the performance hit is a reason enough to write less readable code, especially since the function might well be inlined (and thus the call costs nothing ; the loop may in fact be unrolled and all indexes computed at compile-time, think constexpr).
  • aaronman
    aaronman over 10 years
    @aschepler too often SO is about typing speed (though I actually really suck at typing :) )
  • Tommy
    Tommy over 10 years
    But the code is not unreadable. Perhaps sorting the statements so that array[1] =, array[2]=... this is called loop unrolling and has advantages. Simple > Complicated. I stress again that if there were 300 values or something, then a loop would most definitely be needed.
  • Matthew D. Scholefield
    Matthew D. Scholefield over 10 years
    This seems like it would be simpler, except sadly, the compiler I'm using gives me the error: sorry, unimplemented: non-trivial designated initializers not supported.
  • syam
    syam over 10 years
    @Tommy Well, "unreadable" is a matter of opinion. Repeating the same variable name 30 times is definitely unreadable (rather unmaintainable, actually) in my book.
  • Matthew D. Scholefield
    Matthew D. Scholefield over 10 years
    Yes, like you mentioned, I am doing this for readiblity/simplicity, rather than for efficiency.
  • aschepler
    aschepler over 10 years
    Not that it's a competition or anything.
  • Tommy
    Tommy over 10 years
    From reading the values op posted, there does not appear to be any easy pattern of the indices ( I tried to find a simple transformation), which is why I suspect a performance hit because transform_into_index() is going to be complicated. Or impossible =).
  • Matthew D. Scholefield
    Matthew D. Scholefield over 10 years
    This is exactly what I need, just when I compile, I get the error: error: 'std::initializer_list' has not been declared along with syntax errors due to that. Any ideas?
  • aaronman
    aaronman over 10 years
    @RandomPerson323 do you have the -std=c++11 flag, what compiler are you using
  • aschepler
    aschepler over 10 years
    And be sure to #include <initializer_list>. (My demo: coliru.stacked-crooked.com/a/9e7e4ed2354a9714 )
  • Matthew D. Scholefield
    Matthew D. Scholefield over 10 years
    I did not set any flag (do you mean in the makefile?), and I am compiling for the nds with devkitPro. When I added the include, the error changed:
  • Matthew D. Scholefield
    Matthew D. Scholefield over 10 years
    Now I get syntax errors. This is the first one: error: expected constructor, destructor, or type conversion before '(' token
  • aaronman
    aaronman over 10 years
    @RandomPerson323 I'm not familiar with devkitpro, but it says right there you need -std=c++11 for this to work it sounds like your compilers out of date since all the good compilers have full c++11 support
  • aaronman
    aaronman over 10 years
    @RandomPerson323 it would be helpful if you edited your question to include exactly what you have, my code compiles fine
  • Matthew D. Scholefield
    Matthew D. Scholefield over 10 years
    I added the flag, and fixed a stupid error I accidentally had, and now it compiles :).
  • aaronman
    aaronman over 10 years
    @RandomPerson323 nice, if possible I recommend you update your compiler to a current one with full c++11 support
  • Matthew D. Scholefield
    Matthew D. Scholefield over 10 years
  • Matthew D. Scholefield
    Matthew D. Scholefield over 10 years
    Although this would not be as readably, it would definitely save space.
  • aaronman
    aaronman over 10 years
    This is slightly disturbing +1
  • Matthew D. Scholefield
    Matthew D. Scholefield over 10 years
    Lol, yes, this it crazy. One question: Would this be more efficient (At running the program, not typing), and if so, by how much?
  • goji
    goji over 10 years
    @Dietmar Kühl Interesting. I must play around with the literal operator sometime. Can you see any problems with my answer?
  • goji
    goji over 10 years
    I like this one best for simplicity, altho coding mine was fun ;)
  • PaperBirdMaster
    PaperBirdMaster over 10 years
    I liked this one! (though i hate va_lists) but i've tested the code on ideone ( ideone.com/tCma9B ) and doesn't works: it doesn't initialize the first index passed on selection.
  • Abhijit
    Abhijit over 10 years
    @PaperBirdMaster: The first index is the length rather than the Index, so it will not be initialized. As you might recollect, the way argument passing works, there should be someway for the recipient to know what is the data type and no of data passed. The first number in the list actually does it.
  • Abhijit
    Abhijit over 10 years
    @PaperBirdMaster: I understand your predicament in using va_list, but unless we have something better initialization list or variadic template, that is the only viable approach.
  • PaperBirdMaster
    PaperBirdMaster over 10 years
    oh! first number is the lenght, sorry for the misunderstanding :O
  • Matthew D. Scholefield
    Matthew D. Scholefield over 10 years
    Although this would be slightly less readable (because of the underscore syntax), how wold it compare speed-wise to aarman's answer?
  • Matthew D. Scholefield
    Matthew D. Scholefield over 10 years
    So, is std::valarray<int> array(30); to create the array?
  • Abhijit
    Abhijit over 10 years
    @RandomPerson323: Yes, that will create an array of 30 integers
  • Matthew D. Scholefield
    Matthew D. Scholefield over 10 years
    OK, when I get the time I will look into this.
  • Abhijit
    Abhijit over 10 years
    @PaperBirdMaster: I have modified the code, to remove the dependency on the length argument
  • Abhijit
    Abhijit over 10 years
    @RandomPerson323: And my guess is, this solution would be comparable in terms of efficiency to the already posted once.
  • Khurshid Normuradov
    Khurshid Normuradov over 10 years
    I agree about less readable. aarman's answer is good, but it can't check array bound. My implement can't check array bound,too, but it can give warning at compile time.
  • KitsuneYMG
    KitsuneYMG over 10 years
    I like this one the best. Due to the way template expansion works, it's closest to the 'manual' way. Since it's all compiler time indexing, there is even the possibility of memset being used for larger contiguous chunks of identical values.
  • Dietmar Kühl
    Dietmar Kühl over 10 years
    @RandomPerson323: The implementation in the answer uses a fairly heavy-weight run-time approach to parsing the string. It could be done at compile-time, emitting constant expression, I think but it wouldn't necessarily be faster. Personally, I'm not quite satisfied with the notation and my main reason for posting it was to show an alternative which may lead to something useful.