String With Array Structure to Array

String with array structure to Array

Given the values

$key = "Main.Sub.SubOfSub";
$target = array();
$value = "SuperData";

Here's some code I have lying around that does what you need¹:

$path = explode('.', $key);
$root = &$target;

while(count($path) > 1) {
$branch = array_shift($path);
if (!isset($root[$branch])) {
$root[$branch] = array();
}

$root = &$root[$branch];
}

$root[$path[0]] = $value;

See it in action.

¹ Actually it does slightly more than that: it can be trivially encapsulated inside a function, and it is configurable on all three input values (you can pass in an array with existing values, and it will expand it as necessary).

Array structures can contain strings, which can be different lengths (and thus different sizes in memory). How is this possible?

In modern programming languages, String data types are implemented as objects, and an array of Strings is in reality an array of references (pointers if you wish) to String objects, and references have a fixed, known size.

In general - in arrays of basic data types, each position will store the actual data. In arrays of objects, each position will contain a reference to the object, which resides somewhere else in memory and can be of any size (memory permitting.)

Create Array of Strings Inside of Struct

Your definition of Player is an example of a C99 extension call flexible array: an array of unknown size that is the last member of a structure and that will be accessible only upto the size actually allocated for each instance. You probably did not mean to use that. And you cannot initialize the values with the syntax used in your spawnPlayer function.

You can define an array of fixed size this way:

typedef struct Player {
char *inventory[3];
} Player;

And you can initialize an allocated instance of Player this way:

Player *spawnPlayer(void) {
Player *stats = malloc(sizeof(*stats));

stats->inventory[0] = "potion";
stats->inventory[1] = "potion";
stats->inventory[2] = "ether";

return stats;
}

If you meant for the array to have a size known at runtime and use a flexible array, you probably want to add a member for the actual size allocated:

typedef struct Player {
int size;
char *inventory[];
} Player;

And you will allocate it and initialize it this way:

Player *spawnPlayer(void) {
Player *stats = malloc(sizeof(*stats) + 3 * sizeof(*stats->inventory));

stats->size = 3;
stats->inventory[0] = "potion";
stats->inventory[1] = "potion";
stats->inventory[2] = "ether";

return stats;
}

Flexible arrays are a C99 extension, you can simulate them in C90 by defining inventory with a size of 0 is the compiler supports it, or a size of 1, but it is not strictly portable.

There is a third possibility, using a pointer to an array of char*:

typedef struct Player {
int size;
char **inventory;
} Player;

Player *spawnPlayer(void) {
Player *stats = malloc(sizeof(*stats));

stats->size = 3;
stats->inventory = malloc(sizeof(*stats->inventory) * 3);
stats->inventory[0] = "potion";
stats->inventory[1] = "potion";
stats->inventory[2] = "ether";

return stats;
}

How to convert array of strings into array of struct with conditions

You can have the desired columns in a list and use it to filter the transformed array :

column_list = ["clm1", "clm2", "clm3", "clm4", "clm6", "clm7", "clm8"]

Now add this filter after the transform step using filter function:

column_filter = ','.join(f"'{c}'" for c in column_list)

transform_expr = f"""
filter(transform(split(_c0, '[|]'), (x, i) ->
struct(
IF(x like '%=%', substring_index(x, '=', 1), concat('clm', i+1)) as name,
substring_index(x, '=', -1) as value
)
), x -> x.name in ({column_filter}))
"""

This will filter out all the columns that are not present in the list.

And finally, add the missing columns as nulls using simple select expression:

df = df.select("_c0",  explode(map_from_entries(expr(transform_expr))).alias("col_name", "col_value")).groupby("_c0").pivot('col_name').agg(first('col_value')).drop("_c0")

## add missing columns as nulls
final_columns = [col(c).alias(c) if c in df.columns else lit(None).alias(c) for c in column_list]

df.select(*final_columns).show()

#+----+----+----+----+----+----+----+
#|clm1|clm2|clm3|clm4|clm6|clm7|clm8|
#+----+----+----+----+----+----+----+
#| a| b| c| 9| 60| 23|null|
#| a| b| c| 1|null|null|null|
#+----+----+----+----+----+----+----+

How to convert array of array (string type) to struct - Spark/Scala?

To create an array of structs given an array of arrays of strings, you can use struct function to build a struct given a list of columns combined with element_at function to extract column element at a specific index of an array.

To solve your specific problem, as you correctly stated you need to do two things:

  • First, transform your string to an array of arrays of strings
  • Then, use this array of arrays of strings to build your struct

In Spark 3.0 and greater

Using Spark 3.0, we can perform all those steps using spark built-in functions.

For the first step, I would do as follows:

  • first remove [[ and ]] from family_name string using regexp_replace function
  • then, create first array level by splitting this string using split function
  • then, create second array level by splitting each element of previous array using transform and split functions

And for the second step, use struct function to build a struct, picking element in arrays using element_at function.

Thus, complete code using Spark 3.0 and greater would be as follows, with data as input dataframe:

import org.apache.spark.sql.functions.{col, element_at, regexp_replace, split, struct, transform}

val result = data
.withColumn(
"family_name",
transform(
split( // first level split
regexp_replace(col("family_name"), "\\[\\[|]]", ""), // remove [[ and ]]
"],\\["
),
x => split(x, ",") // split for each element in first level array
)
)
.withColumn("family_name", transform(col("family_name"), x => struct(
element_at(x, 1).as("f_name"), // index starts at 1
element_at(x, 2).as("l_name"),
element_at(x, 3).as("status"),
element_at(x, -1).as("ph_no"), // get last element of array
)))

In Spark 2.X

Using Spark 2.X, we have to rely on an user-defined function. First, we need to define a case class that represent our struct:

case class FamilyName(
f_name: String,
l_name: String,
status: String,
ph_no: String
)

Then, we define our user-defined function and apply it to our input dataframe:

import org.apache.spark.sql.functions.{col, udf}

val extract_array = udf((familyName: String) => familyName
.replaceAll("\\[\\[|]]", "")
.split("],\\[")
.map(familyName => {
val explodedFamilyName = familyName.split(",", -1)
FamilyName(
f_name = explodedFamilyName(0),
l_name = explodedFamilyName(1),
status = explodedFamilyName(2),
ph_no = explodedFamilyName(explodedFamilyName.length - 1)
)
})
)

val result = data.withColumn("family_name", extract_array(col("family_name")))

Result

If you have the following data dataframe:

+---------------------------------------------------------------+---+
|family_name |id |
+---------------------------------------------------------------+---+
|[[John, Doe, Married, 999-999-9999],[Jane, Doe, Married,Wife,]]|id1|
|[[Tom, Riddle, Single, 888-888-8888]] |id2|
+---------------------------------------------------------------+---+

You get the following result dataframe:

+-----------------------------------------------------------------+---+
|family_name |id |
+-----------------------------------------------------------------+---+
|[{John, Doe, Married, 999-999-9999}, {Jane, Doe, Married, }]|id1|
|[{Tom, Riddle, Single, 888-888-8888}] |id2|
+-----------------------------------------------------------------+---+

having the following schema:

root
|-- family_name: array (nullable = true)
| |-- element: struct (containsNull = false)
| | |-- f_name: string (nullable = true)
| | |-- l_name: string (nullable = true)
| | |-- status: string (nullable = true)
| | |-- ph_no: string (nullable = true)
|-- id: string (nullable = true)

How to store elements of a string array into a struct in C?

First, very good job on the detail, the minimum complete and verifiable example, and the formatting of your question. Next, it is apparent you are quite lost...

It is hard to imagine a more awkward approach to doing what you are attempting to do, but for the sake of learning, there is quite a bit of learning that can be had in doing it in an awkward fashion.

To begin, do not use magic numbers in your code. This is for readability and maintainability as your code grows. 10, 30, 97, 122, 48 and 57 are all magic numbers.

#define CPWT 10     /* if you need a constant, #define one (or more) */
#define NPTR 30 /* (do not use "magic numbers" in your code) */

Do not use magic numbers for characters. While you should use the macros provided in ctype.h for islower() and isdigit(), if you are going to use characters, then use characters, e.g. if (foo >= 'a' && foo <= 'z') not 97 and 122. Just single quote the character.

Next, you are overwriting every pointer in arr every time you assign arr[i+1] = pch; Why?

    char *arr[NPTR];                /* array of 30 UNINITIALIZED pointers */

While strtok does return a pointer, when you assign arr[i+1] = pch;, you are assigning the same pointer to each pointer in arr. When you are done, every element in arr will hold the last value returned by strtok (and since you assign after the last call to strtok returns NULL -- you most likely SegFault)

Further, before you "store" anything at the address of arr[0] to arr[NPTR-1], you must allocate storage based on the length of the string you are storing. (and yes, even though you are only storing a single-character in many elements, you are storing strings -- and every string requires a nul-terminating character).

You cannot assign strings in C (except for the assignment of string literals or during initialization of an array). Otherwise, you must copy strings in C.

So since you begin with uninitialized, unallocated pointers in arr, you must allocate and then copy to store information at each pointer address. You have two ways of doing so, (1) either malloc (length + 1) characters, validate the allocation, and then strcpy (or more efficiently memcpy since you have already scanned to find the nul-character with strlen()), or (2) if you have strdup() it will both allocate and copy as you did in (1) in a single function call. (note: since strdup allocates, you still must validate the allocation succeeded)

Example (1)

    if ((pch = strtok (str," ;=,.-")) != NULL) {    /* validate each call */
size_t len = strlen (pch); /* get length */
if ((arr[0] = malloc (len + 1)) == NULL) { /* allocate/validate */
perror ("arr[0]-malloc");
exit (EXIT_FAILURE);
}
memcpy (arr[0], pch, len+1); /* copy! string to arr[0] */
i++;
}

Example 2 (using strdup to allocate/copy)

    /* only loop if good return from strtok */
while ((pch = strtok (NULL, " ;=,.-")) != NULL)
{
/* allocate/copy all at once with strdup (if you have it) */
if ((arr[i] = strdup (pch)) == NULL) {
perror ("arr[n]-strdup");
exit (EXIT_FAILURE);
}
i++;
}

Since you have just filled i elements of arr, there is no need for:

    int sizeofstring = sizeof(str)/sizeof(str[0]);

You know you have i strings in arr -- use i not sizeofstring and step through each string you stored in arr, not each character in str. That defeats the entire purpose of having tokenized str. Further, you only want to consider single character or single digit strings in arr (not "milk" and "cheese") when setting connectingPoints and weights, so check whether the 2nd character is the nul-character, otherwise skip the element of arr.

You can't use y for both connectingPoints and weights, you have a, b, d connectingPoints (3 of them) and only 2 weights. You would be attempting to access outside the valid data in weights if you iterated from j = 0; j < y; ...

Again, you cannot assign a pointer as a character, so the easiest way to assign the 1st character in a string as a character, is simply to dereference the pointer, e.g. *arr[x] (which is equivalent to the character arr[x][0]). That in mind, you could do:

    /* i contains the number of strings in arr - use it */
for (x = 0; x < i; x++) {
if (arr[x][1] == 0) { /* only consider single char strings in arr */
if (cpts < CPWT && islower(*arr[x])) { /* check bound/lowercase */
myPoint.connectingPoints[cpts] = *arr[x]; /* assign char */
cpts++;
}
else if (weights < CPWT && isdigit(*arr[x])) { /* same w/digits */
myPoint.weights[weights] = *arr[x];
weights++;
}
}
}

(note: the separate use of the counters cpts and weights instead of a single y)

Putting all the pieces together, you could do the awkward approach with something like the following:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define CPWT 10 /* if you need a constant, #define one (or more) */
#define NPTR 30 /* (do not use "magic numbers" in your code) */

struct stopPoints {
int weights[CPWT];
char connectingPoints[CPWT];
char *items;
int startBool;
};

int main (void)
{
struct stopPoints myPoint = { .weights = {0} };
char *arr[NPTR]; /* array of 30 UNINITIALIZED pointers */
char str[] ="a = 2.b 1.d; milk cheese";
char *pch;
int i = 0, x, cpts = 0, weights = 0;

if ((pch = strtok (str," ;=,.-")) != NULL) { /* validate each call */
size_t len = strlen (pch); /* get length */
if ((arr[0] = malloc (len + 1)) == NULL) { /* allocate/validate */
perror ("arr[0]-malloc");
exit (EXIT_FAILURE);
}
memcpy (arr[0], pch, len+1); /* copy! string to arr[0] */
i++;
}

/* only loop if good return from strtok */
while ((pch = strtok (NULL, " ;=,.-")) != NULL)
{
/* allocate/copy all at once with strdup (if you have it) */
if ((arr[i] = strdup (pch)) == NULL) {
perror ("arr[n]-strdup");
exit (EXIT_FAILURE);
}
i++;
}

/* i contains the number of strings in arr - use it */
for (x = 0; x < i; x++) {
if (arr[x][1] == 0) { /* only consider single char strings in arr */
if (cpts < CPWT && islower(*arr[x])) { /* check bound/lowercase */
myPoint.connectingPoints[cpts] = *arr[x]; /* assign char */
cpts++;
}
else if (weights < CPWT && isdigit(*arr[x])) { /* same w/digits */
myPoint.weights[weights] = *arr[x];
weights++;
}
}
}

puts ("arr contents:");
for (x = 0; x < i; x++)
printf (" arr[%2d]: %s\n", x, arr[x]);

puts ("\nconnectingPoints:");
for (x = 0; x < cpts; x++)
printf (" myPoint.connectionPoints[%2d]: %c\n",
x, myPoint.connectingPoints[x]);

puts ("\nweights:");
for (x = 0; x < weights; x++)
printf (" myPoint.weights[%2d]: %c\n",
x, myPoint.weights[x]);

return 0;
}

(note: the use of islower() and isdigit() from ctype.h)

Example Use/Output

$ ./bin/strtokarrptrs
arr contents:
arr[ 0]: a
arr[ 1]: 2
arr[ 2]: b
arr[ 3]: 1
arr[ 4]: d
arr[ 5]: milk
arr[ 6]: cheese

connectingPoints:
myPoint.connectionPoints[ 0]: a
myPoint.connectionPoints[ 1]: b
myPoint.connectionPoints[ 2]: d

weights:
myPoint.weights[ 0]: 2
myPoint.weights[ 1]: 1

Look things over and let me know if you have further questions.

How to convert a string with arrays to an array

Right, most answers posted here suggest using JSON.parse and then get downvoted 3 times before getting deleted. What people overlook here is the lack of JSON-compliant quotation. The string IS, however, valid JavaScript. You can do the following:





const obj = {

thing: "['store_owner', 'super_admin']",

otherThing: "['apple', 'cookies']"

}


for (const key in obj) {

const value = obj[key];

obj[key] = eval(value);

}


console.log(obj);

Pass an element from a structs array and concatenate a string in C

The functions that are assigned to each element need to take an element* as input, as they need to be able to modify the data member as needed. This is especially important for "string" elements, since you need to be able to reallocate the string memory when adding more characters, which means assigning a new char* pointer to the data.

You can pass in the element* pointers as void* parameters (which you said is required), but you will have to type-cast the void* back to element* inside the function bodies.

Try something like this:

typedef struct
{
void *data;
void (*print)(void *);
status_ty (*add)(void *, int);
void (*free_element)(void *);
} element_ty;

/*------------------------------------------------------------------*/

void FreeStrElement(void *element)
{
element_ty *p_element = (element_ty *) element;
free(p_element->data);
}

/*------------------------------------------------------------------*/

status_ty AddToInt(void *element, int IncrementValue)
{
element_ty *p_element = (element_ty *) element;
int *p_value = (int*) &(p_element->data);
*p_value += IncrementValue;
return ...;
}

/*------------------------------------------------------------------*/

status_ty AddToFlt(void *element, int IncrementValue)
{
element_ty *p_element = (element_ty *) element;
float *p_value = (float*) &(p_element->data);
*p_value += IncrementValue;
return ...;
}

/*------------------------------------------------------------------*/

status_ty AddToStr(void *element, int IncrementValue)
{
element_ty *p_element = (element_ty *) element;
char *p_str = (char*) p_element->data;

char *new_str = realloc(p_str, strlen(p_str) + 13);
if (!new_str) return ...;

sprintf(new_str, "%s%d", p_str, IncrementValue);

p_element->data = new_str;

return ...;
}

/*------------------------------------------------------------------*/

void PrintInt(void *element)
{
element_ty *p_element = (element_ty *) element;
int *p_value = (int*) &(p_element->data);
printf("Integer in the element: %d\n", *p_value);
}

/*------------------------------------------------------------------*/

void PrintFlt(void *element)
{
element_ty *p_element = (element_ty *) element;
float *p_value = (float*) &(p_element->data);
printf("Float in the element: %f\n", *p_value);
}

/*------------------------------------------------------------------*/

void PrintStr(void *element)
{
element_ty *p_element = (element_ty *) element;
char *p_str = (char*) p_element->data;
printf("String in the element: %s\n", p_str);
}

/*------------------------------------------------------------------*/

status_ty InitInt(int value, element_ty *element)
{
int *p_value = (int*) &(element->data);
*p_value = value;
element->print = &PrintInt;
element->add = &AddToInt;
element->free_element = NULL;

return ...;
}

/*------------------------------------------------------------------*/

status_ty InitFlt(float value, element_ty *element)
{
float *p_value = (float*) &(element->data);
*p_value = value;
element->print = &PrintFlt;
element->add = &AddToFlt;
element->free_element = NULL;

return ...;
}

/*------------------------------------------------------------------*/

status_ty InitStr(const char *value, element_ty *element)
{
element->data = strdup(value);
element->print = &PrintStr;
element->add = &AddToStr;
element->free_element = &FreeStrElement;

return ...;
}

/*------------------------------------------------------------------*/

Then you can do things like this:

void add (int value, element_ty *elements, int n) {
for(int i = 0 i < n; ++i) {
element_ty *elem = &elements[i];
elem->add(elem, value);
}
}

void print (element_ty *elements, int n) {
for(int i = 0 i < n; ++i) {
element_ty *elem = &elements[i];
elem->print(elem);
}
}

void free_elements (element_ty *elements, int n) {
for(int i = 0 i < n; ++i) {
element_ty *elem = &elements[i];
if (elem->free_element) elem->free_element(elem);
}
}

That being said, this approach assumes sizeof(int) and sizeof(float) are <= sizeof(void*), which is usually the case, but not guaranteed.

This approach also violates Strict Aliasing rules. You can use memcpy() to avoid that, but using a union instead would be easier and cleaner (it also avoids the sizeof issue, too), eg:

typedef struct
{
union {
int i;
float f;
char *s;
} data;
void (*print)(void *);
status_ty (*add)(void *, int);
void (*free_element)(void *);
} element_ty;

/*------------------------------------------------------------------*/

void FreeStrElement(void *element)
{
element_ty *p_element = (element_ty *) element;
free(p_element->data.s);
}

/*------------------------------------------------------------------*/

status_ty AddToInt(void *element, int IncrementValue)
{
element_ty *p_element = (element_ty *) element;
p_element->data.i += IncrementValue;
return ...;
}

/*------------------------------------------------------------------*/

status_ty AddToFlt(void *element, int IncrementValue)
{
element_ty *p_element = (element_ty *) element;
p_element->data.f += IncrementValue;
return ...;
}

/*------------------------------------------------------------------*/

status_ty AddToStr(void *element, int IncrementValue)
{
element_ty *p_element = (element_ty *) element;
char *p_str = p_element->data.s;

char *new_str = realloc(p_str, strlen(p_str) + 13);
if (!new_str) return ...;

sprintf(new_str, "%s%d", p_str, IncrementValue);

p_element->data.s = new_str;

return ...;
}

/*------------------------------------------------------------------*/

void PrintInt(void *element)
{
element_ty *p_element = (element_ty *) element;
printf("Integer in the element: %d\n", p_element->data.i);
}

/*------------------------------------------------------------------*/

void PrintFlt(void *element)
{
element_ty *p_element = (element_ty *) element;
printf("Float in the element: %f\n", p_element->data.f);
}

/*------------------------------------------------------------------*/

void PrintStr(void *element)
{
element_ty *p_element = (element_ty *) element;
printf("String in the element: %s\n", p_element->data.s);
}

/*------------------------------------------------------------------*/

status_ty InitInt(int value, element_ty *element)
{
element->data.i = value;
element->print = &PrintInt;
element->add = &AddToInt;
element->free_element = NULL;

return ...;
}

/*------------------------------------------------------------------*/

status_ty InitFlt(float value, element_ty *element)
{
element->data.f = value;
element->print = &PrintFlt;
element->add = &AddToFlt;
element->free_element = NULL;

return ...;
}

/*------------------------------------------------------------------*/

status_ty InitStr(const char *value, element_ty *element)
{
element->data.s = strdup(value);
element->print = &PrintStr;
element->add = &AddToStr;
element->free_element = &FreeStrElement;

return ...;
}

/*------------------------------------------------------------------*/

array of strings within a struct in C

The first assignment is correct.

The second one is not. You need to dynamically allocate the array:

ext[0].pname = malloc( sizeof(char*) * 5 );
ext[0].pname[0] = "A";
ext[0].pname[1] = "B";
//and so on
//you can use a loop for this


Related Topics



Leave a reply



Submit