Combine list of json data based on ID - c#

I have List of JArray like below trying to combine/concatenate the data based on ID
[{"ID":"A1","color1":"silver"}, {"ID": "A3", "color1": "Yellow"}]
[{"ID":"A1","color2":"blue","color3":"pink"}]
[{"ID":"A2","color4":"green"}]
[{"ID":"A1","color5":"red","color6":"aqua"}]
[{"ID":"A1","description":"looking for more colors"}]
Trying for an output like this
[{"ID":"A1",
"color1":"silver",
"color2":"blue",
"color3":"pink",
"color4": "",
"color5":"red",
"color6":"aqua",
"description":"looking for more colors"
},
{"ID":"A2",
"color1":"",
"color2":"",
"color3":"",
"color4": "green",
"color5":"",
"color6":"",
"description":""
},
{
"ID": "A3",
"color1": "yellow",
"color2":"",
"color3":"",
"color4": "",
"color5":"",
"color6":"",
"description":""
}
]
Any suggestion or help into the right direction please.
List<JArray> data = new List<JArray>();
JArray array = JArray.Parse(s); // s is [{"ID":"A1","color1":"silver"}]
data.Add(array);
for (int x = 0; x < data.Count() - 1; x++)
{
data[x].Merge(data[x + 1], new JsonMergeSettings
{
MergeArrayHandling = MergeArrayHandling.Union
});
var groupedByUserID = data[x].GroupBy(x => x["ID"]).ToList();
foreach (var item in groupedByUserID)
{
var firstToken = item.First();
var remainingToken = item.Values().Except(firstToken);
foreach (var i in remainingToken)
{
if (i.Type == JTokenType.Property)
firstToken[((JProperty)i).Name] = ((JProperty)i).Value;
}
jArray.Add(firstToken);
}
}
var result = jArray.ToString(Formatting.Indented);
Console.Write(result);

List<JArray> data = new List<JArray> {
JArray.Parse("[{ 'ID':'A1', 'color1':'silver' }, { 'ID':'A3', 'color1':'Yellow' }]"),
JArray.Parse("[{ 'ID':'A1', 'color2':'blue', 'color3':'pink' }]"),
JArray.Parse("[{ 'ID':'A2', 'color4':'green' }]"),
JArray.Parse("[{ 'ID':'A1', 'color5':'red', 'color6':'aqua' }]"),
JArray.Parse("[{ 'ID':'A1', 'description':'looking for more colors' }]")
};
var names = new HashSet<string>();
foreach (JObject x in data.SelectMany(arr => arr))
{
foreach (var kvp in x)
{
names.Add(kvp.Key);
}
}
var result = data
.SelectMany(arr => arr)
.GroupBy(token => token.Value<string>("ID"))
.Select(group =>
{
var obj = new JObject();
foreach (var name in names)
{
obj[name] = "";
}
foreach (JObject x in group)
{
foreach (var kvp in x)
{
obj[kvp.Key] = kvp.Value;
}
}
return obj;
});
var jArray = new JArray(result);
Console.WriteLine(jArray);

Related

How to split object strings to array of object?

I have a c# object:
var obj = new Item { xcoords="1,2,3", ycoords="5,6,7", zcoords="8,9,4" }
So I want to split this into an array like following:
[
new Item2 { x=1, y=5, z=8 },
new Item2 { x=2, y=6, z=9 },
new Item2 { x=3, y=7, z=4 }
]
Can I do this using Linq? or another way in c#?
Let's create a helper method (either as separate method or as local function):
static IEnumerable<int> ToInts(string s) => s.Split(',').Select(p => Int32.Parse(p));
Putting things together:
var obj = new Item { xcoords = "1,2,3", ycoords = "5,6,7", zcoords = "8,9,4" };
var result = ToInts(obj.xcoords)
.Zip(ToInts(obj.ycoords), ToInts(obj.zcoords))
.Select(t => new Item2{ x = t.First, y = t.Second, z = t.Third });
Note: this overload of Zip (since .NET 6?, the doc is wrong on this point) produces tuples with 3 elements named First, Second and Third.
You can append a .ToArray() if you need an array of Coord.
Test assuming that ToString is overridden like this in Item2:
public override string ToString() => $"[{x}, {y}, {z}]";
foreach (var item in result) {
Console.WriteLine(item);
}
yields:
[1, 5, 8]
[2, 6, 9]
[3, 7, 4]
You don't need LINQ here if coords number is static.
var obj = new Item { xcoords="1,2,3", ycoords="5,6,7", zcoords="8,9,4" };
var xcoords = obj.xcoords.Split(',');
var ycoords = obj.ycoords.Split(',');
var zcoords = obj.zcoords.Split(',');
var result = new Item2[]
{
new Item2 { x = int.Parse(xcoords[0]), y = int.Parse(ycoords[0]), z = int.Parse(zcoords[0]) },
new Item2 { x = int.Parse(xcoords[1]), y = int.Parse(ycoords[1]), z = int.Parse(zcoords[1]) },
new Item2 { x = int.Parse(xcoords[2]), y = int.Parse(ycoords[2]), z = int.Parse(zcoords[2]) },
};
If coords number is dynamic but same for x, y and z then simple "for" loop may resolve the problem.
var obj = new Item { xcoords="1,2,3,4", ycoords="5,6,7,8", zcoords="8,9,4,10" };
var xcoords = obj.xcoords.Split(',');
var ycoords = obj.ycoords.Split(',');
var zcoords = obj.zcoords.Split(',');
var result = new Item2[xcoords.Length];
for (var i = 0; i < xcoords.Length; i++)
{
result[i] = new Item2
{
x = int.Parse(xcoords[i]),
y = int.Parse(ycoords[i]),
z = int.Parse(zcoords[i])
};
}
LINQ version (Requires .NET 6 or higher. In the previous versions Zip method takes only 2 collections at once.):
var obj = new Item { xcoords = "1,2,3,4", ycoords = "5,6,7,8", zcoords = "8,9,4,10" };
var result = Enumerable
.Zip(
obj.xcoords.Split(','),
obj.ycoords.Split(','),
obj.zcoords.Split(','),
(x, y, z) => (x, y, z))
.Select(
coord => new Item2
{
x = int.Parse(coord.x),
y = int.Parse(coord.y),
z = int.Parse(coord.z)
});
Linq approach https://dotnetfiddle.net/CHizmw
Item obj = new Item { xcoords = "1,2,3", ycoords = "5,6,7", zcoords = "8,9,4" };
Func<string, int[]> GetInt = value => value.Split(',').Select(int.Parse).ToArray();
var helper = new { xc = GetInt(obj.xcoords), yc = GetInt(obj.ycoords), zc = GetInt(obj.zcoords) };
Item2[] obj2 = Enumerable.Range(0, helper.xc.Length).Select(o => new Item2() { x = helper.xc[o], y = helper.yc[o], z = helper.zc[o] }).ToArray();

How to get except between two Dictionaries<int, IEnumerable<string>>?

I have two Dictionaries>
var test1 = await GetTest1();
var test2 = await GetTest2();
var groupedTest1 = test1.GroupBy(j => j.someField1)
.ToDictionary(k => k.Key, d => d.Select(s => s.someField2));
var groupedTest2 = test2.GroupBy(a => a.someField1)
.ToDictionary(k => k.Key, d => d.Select(s => s.someField2));
And I need two get the difference between them.
For example:
var result = groupedTest1.Except(groupedTest2);
If groupedTest1 contains something IEnumerable which contains in groupedTest2 I don't need to include this. Besides, I need to include check only for a similar key. My question is:
How can I do it?
groupedTest1 : { { Key: 1, IEnumerable: "test1, test2" },
{ Key: 2, IEnumerable: "test3, test4" } } groupedTest2 : {
{ Key: 2, IEnumerable: "test3, test4" } }
result should be Key: 1, IEnumerable: "test1, test2"
If I understand it you effectively have 2 actions; first, get all the keys that don't match and their entire entry should be in the result, and second for matching keys only get the values that don't match. I've put together a little console app to display this:
static void Main(string[] args)
{
//2 test dictionaries. Key 1 and it's values should be displayed and Key 3 with only "test5".
var test1 = new Dictionary<int, IEnumerable<string>>
{
{ 1, new List<string>{ "test1", "test2" } },
{ 2, new List<string>{ "test3", "test4" } },
{ 3, new List<string>{ "test3", "test4", "test5" } }
};
var test2 = new Dictionary<int, IEnumerable<string>>
{
{ 2, new List<string>{ "test3", "test4" } },
{ 3, new List<string>{ "test3", "test4" } }
};
//get non-matching keys first.
Console.WriteLine("Non-matching keys:");
var entriesWithNoMatch = test1.Where(x => !test2.ContainsKey(x.Key));
foreach (var entry in entriesWithNoMatch)
{
WriteResults(entry.Key, entry.Value.ToList());
}
//get matching keys and compare values
Console.WriteLine("Matching keys, non-matching values:");
var matchingKeys = test1.Keys.Intersect(test2.Keys);
foreach (var match in matchingKeys)
{
var result = new KeyValuePair<int, IEnumerable<string>>(match, test1[match].Except(test2[match]));
if (result.Value.Count() > 0)
{
WriteResults(match, result.Value.ToList());
}
}
Console.ReadLine();
}
//simple method to loop through results displaying key and results.
static void WriteResults(int key, IList<string> results)
{
var sb = new StringBuilder();
foreach (var r in results)
{
sb.Append(r + ",");
}
if (sb.Length > 1)
{
sb.Remove(sb.Length - 1, 1);
}
Console.WriteLine("Key: " + key.ToString() + ", results: " + sb.ToString());
sb.Clear();
}
Results:

how to set Number from 0 to increment 1,2,,... etc for combine dictionary

I have 2 dictionaries where Number = 0 for all items,
var dict1 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key2", new Test { Number = 0, Name = "Name2" } },
{ "Key3", new Test { Number = 0, Name = "Name3" } }
};
var dict2 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key4", new Test { Number = 0, Name = "Name4" } }
};
Now after eliminating duplicate key/value pairs, in combined dictionary result I want to set Number = 1, 2, 3,... how to do this?
var combine = dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select(grp => grp.First())
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
You can do
var n = 0;
and then do it functionally but not very efficiently in your case. The function will select all items from your dictionary and create a new collection with updated values, which is then converted to a dictionary.
var newDict = dict2.Select(d => new Test { Number = ++n, Name = d.Value[1].Name }).ToDictionary();
Or with a good old loop:
foreach(var d in dict2)
{
d.Value[0].Number = n++;
}
As suggested by the comment. If you want to start off with 0, use
n++;
if with 1, use
++n;
Try this:
int i = 0;
var combine = dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.OrderBy(kvp => kvp.Key)
.ToDictionary(kvp => kvp.Key, kvp => new Test() { Number = ++i, Name = kvp.First().Value.Name });
It should give you this:
{ "Key1", new Test { Number = 1, Name = "Name1" } },
{ "Key2", new Test { Number = 2, Name = "Name2" } },
{ "Key3", new Test { Number = 3, Name = "Name3" } }
{ "Key4", new Test { Number = 4, Name = "Name4" } }
I think you're trying to combine dictionaries, and then assign to Number the count of duplicate items.
You might consider putting all the dictionaries into a list and iterating over each item and putting it in a combined result dictionary. If the item already exists in the result then increment the Number property.
Initial setup:
public class Test
{
public int Number { get; set; }
public string Name { get; set; }
}
var dict1 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key2", new Test { Number = 0, Name = "Name2" } },
{ "Key3", new Test { Number = 0, Name = "Name3" } }
};
var dict2 = new Dictionary<string, Test>
{
{ "Key1", new Test { Number = 0, Name = "Name1" } },
{ "Key4", new Test { Number = 0, Name = "Name4" } }
};
// Put the dictionaries you want to combine into one list:
var all = new List<Dictionary<string, Test>>();
all.Add(dict1);
all.Add(dict2);
// Declare result dictionary
var combine = new Dictionary<string, Test>();
Set up is done, this is the main loop you want:
foreach (var dict in all)
{
foreach (var kvp in dict)
{
if (combine.ContainsKey(kvp.Key))
{
combine[kvp.Key].Number++;
}
else
{
combine.Add(kvp.Key, kvp.Value);
}
}
}
Interactive shell output:
Dictionary<string, Submission#0.Test>(4) {
{ "Key1", Submission#0.Test { Name="Name1", Number=1 } },
{ "Key2", Submission#0.Test { Name="Name2", Number=0 } },
{ "Key3", Submission#0.Test { Name="Name3", Number=0 } },
{ "Key4", Submission#0.Test { Name="Name4", Number=0 } }
}
There is an overload of Select extension which provides index as well (MSDN)
var combine = dict1.Union(dict2)
.GroupBy(kvp => kvp.Key)
.Select((grp,index) => new { Key = grp.Key, Value = new Test { Number = index+1, Name = grp.First().Name}})
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
You can loop through the keys
int n = 1;
foreach (string key in combine.Keys) {
combine[key].Number = n++;
}
The keys are not returned in order. If you want to number them in order:
int n = 1;
var orderedKeyValuePairs = combine
.OrderBy(kvp => kvp.Key, StringComparer.CurrentCultureIgnoreCase);
foreach (var kvp in orderedKeyValuePairs) {
kvp.Value.Number = n++;
}
Note that you can only access the Number like this if Test is a reference type (class). If Test was a struct, you would have to re-assign the whole struct because the dictionary would return a copy of the value.
The optional StringComparer argument allows you to specify different string comparison modes:
Ordinal, OrdinalIgnoreCase, CurrentCulture, CurrentCultureIgnoreCase
If you want to sort by name:
int n = 1;
var orderedValues = combine.Values
.OrderBy(v => v.Name, StringComparer.CurrentCultureIgnoreCase);
foreach (var v in orderedValues) {
v.Number = n++;
}
Looping over the key-value-pairs or values also has the advantage that you can change the value directly, whereas when looping through the keys (as in my first code snippet), you must look up the dictionary, which is less performing.

jtoken.selecttokens issue when comparing value of one array with another array

i need to fetch amount using following logic. if product.ID equals stores.ID then fetch product.amount
Json
{
"stores": [
{
"ID": 17736791,
"Name": "ABC"
},
{
"ID": 154423041,
"Name": "XYZ"
}
],
"product": [
{
"ID": 154423041,
"Amount": 19865337
}
]
}
i am using jtoken.selecttoken to fetch data as below. but it throws error as could not read query operator.
string path = ToJsonPath(product[ID=stores[*].ID].Amount);
var data= token.SelectTokens(path)
Updated, ToJsonPath
public string ToJsonPath(string query)
{
string normalizedQuery = query.Replace(DoubleQuotes, SingleQuotes);
StringBuilder jsonPath = new StringBuilder();
jsonPath.Append(string.Concat(RootElement, ChildOperator));
jsonPath.Append(normalizedQuery);
MatchCollection expressions = Regex.Matches(normalizedQuery, ExpressionRegexPattern);
StringBuilder expression = new StringBuilder();
for (int i = 0; i < expressions.Count; i++)
{
if (!Regex.IsMatch(expressions[i].Value, OperatorRegexPattern))
{
continue;
}
expression.Length = 0;
expression.Capacity = 0;
expression.Append(expressions[i].Value);
jsonPath.Replace(expression.ToString(), Placeholder);
string[] expressionTerms = expression.ToString()
.Split(new[] { AndOperator, OrOperator }, StringSplitOptions.RemoveEmptyEntries)
.Select(t => t.Trim())
.ToArray();
foreach (string expressionTerm in expressionTerms)
{
expression.Replace(expressionTerm, Placeholder);
expression.Replace(Placeholder, string.Concat(CurrentElement, ChildOperator, expressionTerm));
}
string expressionWithEscapedOperators = Regex.Replace(expression.ToString(), OperatorRegexPattern, " $& ");
string expressionWithDoubleEqualOperators = Regex.Replace(expressionWithEscapedOperators, EqualOperatorPattern, "$&$&");
string jsonExpression = string.Format(JsonExpressionTemplate, expressionWithDoubleEqualOperators);
jsonPath.Replace(Placeholder, jsonExpression);
}
return jsonPath.ToString();
}
Not sure about JSONPath but with LINQ to JSON this can be achieved as follows:
var obj = JObject.Parse(json);
var storeIds = obj["stores"]
.Select(s => (int)s["ID"])
.ToList();
var selectedAmount = obj["product"]
.Where(p => storeIds.Contains((int)p["ID"]))
.Select(p => (int)p["Amount"])
.FirstOrDefault();
Demo: https://dotnetfiddle.net/CRn5Az

Best algorithm to determine added and removed items when comparing to collections

I am looking for the best algorithm to compare 2 collections and determine which element got added and which element got removed.
private string GetInvolvementLogging(ICollection<UserInvolvement> newInvolvement, ICollection<UserInvolvement> oldInvolvement)
{
//I defined the new and old dictionary's for you to know what useful data is inside UserInvolvement.
//Both are Dictionary<int, int>, because The Involvement is just a enum flag. Integer. UserId is also Integer.
var newDict = newInvolvement.ToDictionary(x => x.UserID, x => x.Involvement);
var oldDict = oldInvolvement.ToDictionary(x => x.UserID, x => x.Involvement);
//I Want to compare new to old -> and get 2 dictionaries: added and removed.
var usersAdded = new Dictionary<int, Involvement>();
var usersRemoved = new Dictionary<int, Involvement>();
//What is the best algoritm to accomplish this?
return GetInvolvementLogging(usersAdded, usersRemoved);
}
private string GetInvolvementLogging(Dictionary<int, Involvement> usersAdded, Dictionary<int, Involvement> usersRemoved)
{
//TODO: generate a string based on those dictionaries.
return "Change in userinvolvement: ";
}
Added elements are only in newDict removed only in oldDict
var intersection = newDict.Keys.Intersect(oldDict.Keys);
var added = newDict.Keys.Except(intersection);
var removed = oldDict.Keys.Except(intersection);
EDIT
I modify your base function, dictionaries is no neded.
Example UserInvolvement implementation
class UserInvolvement
{
public int UserId;
public string Name;
public string OtherInfo;
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
UserInvolvement p = obj as UserInvolvement;
if ((System.Object)p == null)
{
return false;
}
return (UserId == p.UserId) && (Name == p.Name) && (OtherInfo == p.OtherInfo);
}
public override string ToString()
{
return $"{UserId} - {Name} - {OtherInfo}";
}
}
And example function:
private static string GetInvolvementLogging(ICollection<UserInvolvement> newInvolvement,
ICollection<UserInvolvement> oldInvolvement)
{
var intersection = newInvolvement.Select(x => x.UserId).Intersect(oldInvolvement.Select(x => x.UserId));
var addedIds = newInvolvement.Select(x => x.UserId).Except(intersection);
var removedIds = oldInvolvement.Select(x => x.UserId).Except(intersection);
List<UserInvolvement> modifiedUI = new List<UserInvolvement>();
foreach (var i in intersection)
{
var ni = newInvolvement.First(a => a.UserId == i);
var oi = oldInvolvement.First(a => a.UserId == i);
if (!ni.Equals(oi))
{
modifiedUI.Add(ni);
}
}
List<UserInvolvement> addedUI = newInvolvement.Where(x => addedIds.Contains(x.UserId)).Select(w => w).ToList();
List<UserInvolvement> removedUI = oldInvolvement.Where(x => removedIds.Contains(x.UserId)).Select(w => w).ToList();
StringBuilder sb = new StringBuilder();
sb.AppendLine("Added");
foreach (var added in addedUI)
{
sb.AppendLine(added.ToString());
}
sb.AppendLine("Removed");
foreach (var removed in removedUI)
{
sb.AppendLine(removed.ToString());
}
sb.AppendLine("Modified");
foreach (var modified in modifiedUI)
{
sb.AppendLine(modified.ToString());
}
return sb.ToString();
}
And my test function:
static void Main(string[] args)
{
List<UserInvolvement> newUI = new List<UserInvolvement>()
{
new UserInvolvement()
{
UserId = 1,
Name = "AAA",
OtherInfo = "QQQ"
},
new UserInvolvement()
{
UserId = 2,
Name = "BBB",
OtherInfo = "123"
},
new UserInvolvement()
{
UserId = 4,
Name = "DDD",
OtherInfo = "123ert"
}
};
List<UserInvolvement> oldUI = new List<UserInvolvement>()
{
new UserInvolvement()
{
UserId = 2,
Name = "BBBC",
OtherInfo = "123"
},
new UserInvolvement()
{
UserId = 3,
Name = "CCC",
OtherInfo = "QQ44"
},
new UserInvolvement()
{
UserId = 4,
Name = "DDD",
OtherInfo = "123ert"
}
};
string resp = GetInvolvementLogging(newUI, oldUI);
WriteLine(resp);
ReadKey();
WriteLine("CU");
}
Result is:
Added
1 - AAA - QQQ
Removed
3 - CCC - QQ44
Modified
2 - BBB - 123
You could try with Linq:
var usersAdded = newDict.Except(oldDict);
var usersRemoved = oldDict.Except(newDict);
If you need dictionaries as a result you can cast:
var usersAdded = newDict.Except(oldDict).ToDictionary(x => x.Key, x => x.Value);
var usersRemoved = oldDict.Except(newDict).ToDictionary(x => x.Key, x => x.Value);
Think best algorithm will be
foreach (var newItem in newDict)
if (!oldDict.ContainsKey(newItem.Key) || oldDict[newItem.Key]!=newItem.Value)
usersAdded.Add(newItem.Key, newItem.Value);
foreach (var oldItem in oldDict)
if (!newDict.ContainsKey(oldItem.Key) || newDict[oldItem.Key]!=oldItem.Value)
usersRemoved.Add(oldItem.Key, oldItem.Value);
Finally this is my implementation of GetInvolvementLogging:
(the implementation of the string builder method is irrelevant for my question here)
private string GetInvolvementLogging(ICollection<UserInvolvement> newInvolvement, ICollection<UserInvolvement> oldInvolvement)
{
//I defined the new and old dictionary's to focus on the relevant data inside UserInvolvement.
var newDict = newInvolvement.ToDictionary(x => x.UserID, x => (Involvement)x.Involvement);
var oldDict = oldInvolvement.ToDictionary(x => x.UserID, x => (Involvement)x.Involvement);
var intersection = newDict.Keys.Intersect(oldDict.Keys); //These are the id's of the users that were and remain involved.
var usersAdded = newDict.Keys.Except(intersection);
var usersRemoved = oldDict.Keys.Except(intersection);
var addedInvolvement = newDict.Where(x => usersAdded.Contains(x.Key)).ToDictionary(x => x.Key, x => x.Value);
var removedInvolvement = oldDict.Where(x => usersRemoved.Contains(x.Key)).ToDictionary(x => x.Key, x => x.Value);
//Check if the already involved users have a changed involvement.
foreach(var userId in intersection)
{
var newInvolvementFlags = newDict[userId];
var oldInvolvementFlags = oldDict[userId];
if ((int)newInvolvementFlags != (int)oldInvolvementFlags)
{
var xor = newInvolvementFlags ^ oldInvolvementFlags;
var added = newInvolvementFlags & xor;
var removed = oldInvolvementFlags & xor;
if (added != 0)
{
addedInvolvement.Add(userId, added);
}
if (removed != 0)
{
removedInvolvement.Add(userId, removed);
}
}
}
return GetInvolvementLogging(addedInvolvement, removedInvolvement);
}

Categories