1

Basically I have a struct with several members, named a-z. I have a string where I want each letter to correspond to the correct struct member. Currently I use a switch statement with each case in order to access the correct letter member of the struct, but Im wondering if theres a better (cleaner/shorter code) way to do this, without having to put 26 case statements with nearly identical code inside? My code looks somewhat like this:

typedef struct node
{
    struct node *a;
    struct node *b;
    struct node *c;
    ...
    struct node *z;
}node;

node *nTable[26][26][27];
int main
{
    ...
    
    node *nWord = malloc(sizeof(node));
    node *nPath = nWord;
    nTable[0][0][0] = nWord;

    char *cWord 
    cWord = "abcde";

    for (int n = 0; n < 5; n++)
    {
        nWord = malloc(sizeof(node));

        switch (cWord[n])
           case 'a':
               nPath->a = nWord;
               nPath = nWord;
               break;
           case 'b':
               nPath->b = nWord;
               nPath = nWord;
           ...
           case 'z':
               nPath->z = nWord;  //code is the same for each case, only difference between each is which member its assigned to
               nPath = nWord;
    }
}

Note the above code is a much reduced form of my actual code, so there may be some basic syntax and other errors within this example, but the general purpose of my code should be apparent.

So is there a cleaner way to do this, without using a case statement for each letter? Something simple like "nPath->cWord[n] = code;" would be perfect! (though this as it is, obviously does not work)

Any ideas would be great! Also sorry in advance if Ive left out any important info

  • This char *cWord = {a,b,c,d,e}; is an invalid construction. Provide a code snippet that at least compiles. – Vlad from Moscow Jul 25 '20 at 11:56
  • 3
    Instead of numerpous data members like this struct node *a; you could declare one array. – Vlad from Moscow Jul 25 '20 at 11:59
  • I shouldve added, the purpose for this code is to make a linked list, whereas one node is linked to another node through the letter. Im reading in a text file and adding each word to an array of these nodes if that makes sense? Basically I need it to be dynamic, so an array alone wouldnt work in my case – TravelKidKurty Jul 25 '20 at 12:05
  • Ive updated my code to be a bit more representative of the actual process – TravelKidKurty Jul 25 '20 at 12:20

2 Answers2

2

For starters this construction

char *cWord = {a,b,c,d,e};

is syntactically invalid. You may not initialize a scalar object with a braced list with more than one initializer.

As for you question when you could declare one data member of an array type within the structure instead of numerous data members like

struct node *a;

For example

#define N 26

typedef struct node
{
    struct node *nodes[N];
} node;

And then use the following approach

const char *letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
char *p = strchr( letters, toupper( ( unsigned char )cWord[i] ) );

if ( p != NULL )
{
    nPath->nodes[p - letters] = data;
    // or 
    // nPath->nodes[p - letters] = malloc( sizeof( node ) );
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Oh interesting, I never considered an array inside of the struct, that could do the trick! Ill have to play around with this code and test it out, and will get back to you. Thanks a lot for this suggestion so far! – TravelKidKurty Jul 25 '20 at 12:24
  • It was late, so I held off implementing this till today. All I can say is brilliant. Absolutely brilliant. Reduced nearly a ridiculous **350 lines** of repetitive case statements down to literally **under 10 lines**. Strchr is new to me, this is a great tool to know! Also wow, I had no idea you could do arithmetic on pointer addresses [p – letters] to set an index for an array- very clever and powerful! This is exactly the kind of thing I was looking for, a very powerful combination of 2 things new to me, thank you so much! – TravelKidKurty Jul 26 '20 at 06:31
0

Maybe you can focus on the words: at first you have a linked list of words. And then there will be a linked list for every letter in order do build an array of linked lists of words. Consider this example:

#include <stdio.h>

typedef struct _node
{
    char*           word;
    struct _node*   next;
    struct _node*   prev;
};  // single node contains one word

typedef struct _node Node;

typedef struct 
{
    char        letter;
    unsigned    size;
    unsigned    limit;
    Node*       start;
    Node*       end;

}   _list;

typedef _list   Data;

In this way you have a bit more of structure

See this code

    #include "x25list.h"
    
    int main(int argc, char** argv)
    {
        Data* db[26];
        for (int i = 'a'; i < 'z'; i += 1)
        {   // builds initial linked lists
            int ix = i - 'a';
            db[ix] = (Data*)malloc(sizeof(Data));
            db[ix]->letter = i;
            db[ix]->size = 0;
            db[ix]->end = NULL;
            db[ix]->start = NULL;
        };  // for()
        return 0;
    };


In this way you have a linked list for each letter already built and start to catalog words using the notes on the lists pointed by the value of the letter minus the value of 'a'.


arfneto
  • 1,227
  • 1
  • 6
  • 13
  • Ok this seems like a different approach to what I was doing, so Im intrigued. Im pretty new to lists, so just trying to work through your thinking. As I understand it, your 'Node' struct would conceptually look something like this Word1 <-> Word2 <-> Word3 <-> Word 4, with a link to the next and previous word. Your Data list Im having a little more trouble understanding its functionality, but I see its an array of 26 pointers, holding data for letters a-z. Im just not to sure what the rest of the struct's members are used for, or how its linked to the 'Node' structure? – TravelKidKurty Jul 26 '20 at 05:38
  • Maybe it is because you are focusing on the wrong structure: a **Node** in a linked list is just a Node. It contains a payload, in this case a pointer to char, but it could be just void* pointing to a big structure. And pointers to prev and next. A **linked list** BTOH contains Nodes. And a very convenient **size** var, a pointer to start AND end of the list. To help with your model, also a letter. Then you build an array of lists for the letters so you can consume any text and build the list with the words in the text. You get the word, build a node, get the list using the initial and so – arfneto Jul 26 '20 at 16:39
  • Oh ok, I think I get a better picture of it now, but correct me if Im wrong. So reading in a word such as "eddy", it would get populated into the db array of the Data structure at the e ([4]) position, which would provide a size of 1, and a link to the node that stores the actual word. If then adding another word, such as "escape", it would go back to the db array, see e already has a node in it, update size to 2, go to the "eddy" node, and create a pathway so to speak, both to and from "eddy" and "escape" within the Node structure. – TravelKidKurty Jul 27 '20 at 02:02
  • So basically Data carries the stats on size, start word of list for that letter, and end word of list for that letter. Does that sound about right? – TravelKidKurty Jul 27 '20 at 02:02
  • Following your example, db[4] is a linked list for the words that start with the letter **('a' + 4)'** that means **e**. At any time, **size** will tell how many itens are inside that particular list. **start** will point to the first, **last** to the last one. **db** will not provide a link tho "eddy". Instead it will provide a link to the first and last words that starts with that letter. **db** is a set of all words. – arfneto Jul 27 '20 at 02:53
  • In the example, _list is a generic linked list structure and holds just Nodes. At any time there are **size** nodes and there are pointers to the start an end of the list. Your structure has one **_list** for each letter, in the database **db** array. You do not even need to know that the word is "eddy". If you have a function **char* next_word(FILE*)** and you call **char* one = next_word(infile)** the first letter is **one[0]** – arfneto Jul 27 '20 at 02:55
  • And from above, if you have a function **int insert_word(char* word, _list* linked_list);** you can call it right away using **insert_word(one, db[ one[0] - 'a' ])** if "eddy"is the **one** then one[0] is 'e', 'e' - 'a' is 4 and the word will be inserted in the linked list db[4]. At any time db[i]-size will tell how many words are in the list for the letter ('a'+ i). At the end of the input, db will have a database of all words. – arfneto Jul 27 '20 at 02:56
  • Ok yeah, I think thats about the same as I was saying. I was just using eddy as a specific example, but I see that whatever is the contents of the starting node address is generic. This is an interesting use, I like the ability to add more stats and data to the structure. For my specific case, Im actually using it in the form of a trie (sorry, I didnt know this terminology at time of posting question), so the answer I marked above worked perfectly for my use this time, but I will definitely be using yours for future lists! – TravelKidKurty Jul 27 '20 at 03:12
  • trie is far more efficient to this case, since the prefixes would not be replicated in every word. But your question was about linked lists...:) – arfneto Jul 27 '20 at 04:42
  • A note: the answer you told about builds only an array of arrays of words. That is not a trie... – arfneto Jul 27 '20 at 04:49