Convert non typical json string to JSON - performance

I'm fetching from a random API url and I'm getting a response like this one:
"key='jio3298', age=24, key='oijf032', age=62". How can I turn this non-json string into a list of JSON objects (i.e [{'key': 'jio3298', age: 24}, {'key':'oijf032', 'age':62}]) in an efficient way using JavaScript? I did get this code problem in an interview (one of the part of the problem. I needed that list to loop and filter based on a condition) and it seems my answer was at the very least slow.

you should use
String.prototype.split()
to create an array and then loop on this arry and create pears of key and value.
here an expmple:
const parser = (input) => {
input = input.split(",");
let keyAgeList = [],
output = [];
for (let i of input) {
i = i.replace("'", "");
i = i.replace(" ", "");
i = i.split("=");
if (i[0] === "key") {
keyAgeList[0] = i[1];
} else {
keyAgeList[1] = i[1];
}
if (keyAgeList.length > 1) {
let x = {
key: keyAgeList[0].replace("'", ""),
age: keyAgeList[1].replace("'", ""),
};
output.push(x);
keyAgeList = [];
}
}
console.log(JSON.stringify(output))
return JSON.stringify(output);
};
parser("key='jio3298', age=24, key='oijf032', age=62")

Related

JSLINQ - Distinct from multiple columns

I have a working JSFiddle demo
https://jsfiddle.net/u1fohjxw/
The idea is to create a unique list of items, based on multiple fields.
I know the way I've implemented it could be improved, but need advice on doing it in JSLINQ.
This filtered unique list I then manually loop and add again - this could be done in JSLINQ.
Please indicate how this should be done :
var myList = [
{FirstName:"Chris",LastName:"Pearson"},
{FirstName:"Chris",LastName:"Pearson"},
{FirstName:"Chris",LastName:"Sutherland"},
{FirstName:"John",LastName:"Ronald"},
{FirstName:"Steve",LastName:"Pinkerton"}
];
var exampleArray = JSLINQ(myList)
.Distinct(function(item){ return item.FirstName.concat(";",item.LastName)}).items
var newList = [];
for (var x = 0 ; x < exampleArray.length ; x++) {
var arraylist = exampleArray[x].split(";");
var y= new Object();
y.FirstName = arraylist[0];
y.LastName = arraylist[1];
newList.push(y);
};
how you doing? :)
Maybe something like this helps you out:
var myList = [
{FirstName:"Chris",LastName:"Pearson"},
{FirstName:"Chris",LastName:"Pearson"},
{FirstName:"Chris",LastName:"Sutherland"},
{FirstName:"John",LastName:"Ronald"},
{FirstName:"Steve",LastName:"Pinkerton"}
];
var resultList = myList.Distinct(function(x){
return {
FirstName: x.FirstName,
LastName: x.LastName
}
}).ToArray();
This will return an array of the object returned inside the distinct.
Edit:
Change the distinct method to this:
Distinct: function(clause) {
var item, dict = {}, retVal = [];
for (var i = 0; i < this.items.length; i++) {
item = clause.apply(this.items[i], [this.items[i]]);
if (dict[JSON.stringify(item)] === undefined) {
dict[JSON.stringify(item)] = true;
retVal.push(item);
}
}
dict = null;
return JSLINQ(retVal);
},
It's not stress tested, I don't know how much time will take to iterate through 10k+ objects, but it's something to study and improve! :)
There's another possible fix to this if you want to try.
Cheers!

Extract key value from a string Using Linq

There is a scenario in which I am sending an HTTP request and getting this response String from the server.
submitstatus: 0
smsid: 255242179159525376
var streamResponse = newStreamReader(response.GetResponseStream()).ReadToEnd().ToString();
I want to extract the key values by using LINQ. New to LINQ any suggestions.
I am using output string to simulate the result
string output = #"submitstatus: 0
smsid: 255242179159525376";
// you can use regex to match the key/value
// what comes before `:` will be the key and after the value
var matches = Regex.Matches(output, #"(?<Key>\w+):\s(?<Value>[^\n]+)");
// for each match, select the `Key` match as a Key for the dictionary and
// `Value` match as the value
var d = matches.OfType<Match>()
.ToDictionary(k => k.Groups["Key"].Value, v => v.Groups["Value"].Value);
So you will have a Dictionary<string, string> with keys and values.
Using Split method
var keysValues = output.Split(new string[] { ":", "\r\n" },
StringSplitOptions.RemoveEmptyEntries);
Dictionary<string, string> d = new Dictionary<string, string>();
for (int i = 0; i < keysValues.Length; i += 2)
{
d.Add(keysValues[i], keysValues[i + 1]);
}
Trying to use purely Linq
var keysValues = output.Split(new string[] { ":", "\r\n" },
StringSplitOptions.RemoveEmptyEntries);
var keys = keysValues.Where((o, i) => (i & 1) == 0);
var values = keysValues.Where((o, i) => (i & 1) != 0);
var dictionary = keys.Zip(values, (k, v) => new { k, v })
.ToDictionary(o => o.k, o => o.v);
Why dont you use regex? Smth like:
(?<=submitstatus:\s)\d+
for submitstatus
and
(?<=smsid:\s)\d+
for smsid

How do I group by sequence in LINQ?

Given sequence :
["1","A","B","C","2","F","K","L","5","6","P","I","E"]
The numbers represent items that I identify as headers, whereas the letters represent items that I identify as data. I want to associate them into groups like this.
1:A,B,C
2:F,K,L
5:
6:P,I,E
I can easily achieve this using a foreach or while loop on the enumerator, but is there a LINQ'ish way to achieve this? This is a recurring pattern in my domain.
Here's a solution with LINQ. It's a little bit complicated though. There may be room for some tricks. It doesn't look that terrible but it can be more readable with a foreach loop.
int lastHeaderIndex = default(int);
Dictionary<string, IEnumerable<string>> groupedItems =
items.Select((text, index) =>
{
int number;
if (int.TryParse(text, out number))
{
lastHeaderIndex = index;
}
return new { HeaderIndex = lastHeaderIndex, Value = text };
})
.GroupBy(item => item.HeaderIndex)
.ToDictionary(item => item.FirstOrDefault().Value,
item => item.Skip(1).Select(arg => arg.Value));
You can make use of a fold:
var aggr = new List<Tuple<Int,List<String>>>();
var res = sequence.Aggregate(aggr, (d, x) => {
int i;
if (Int32.TryParse(x, out i)) {
var newDict = d.Add(new Tuple(i, new List<string>()));
return newDict;
}
else {
var newDict = d[d.Count - 1].Item2.Add(x);
return newDict;
}
}).ToDictionary(x => x.Item1, x => x.Item2);
However, this doesn't look so nice, since there's lacking support for immutable values. Also, I couldn't test this right now.
foreach loop with int.TryParse should help. 'GroupBy' from LINQ won't help here much.
Since this a common pattern in your domain, consider streaming the results instead of gathering them all into a large in-memory object.
public static IEnumerable<IList<string>> SplitOnToken(IEnumerable<string> input, Func<string,bool> isSplitToken)
{
var set = new List<string>();
foreach(var item in input)
{
if (isSplitToken(item) && set.Any())
{
yield return set;
set = new List<string>();
}
set.Add(item);
}
if (set.Any())
{
yield return set;
}
}
Sample usage:
var sequence = new[] { "1", "A", "B", "C", "2", "F", "K", "L", "5", "6", "P", "I", "E" };
var groups = SplitOnToken(sequence, x => Char.IsDigit(x[0]));
foreach (var #group in groups)
{
Console.WriteLine("{0}: {1}", #group[0], String.Join(" ", #group.Skip(1).ToArray()));
}
output:
1: A B C
2: F K L
5:
6: P I E
Here's what I ended up using. Pretty much the same structure as phg's answer.
Basically, it is an aggregate function that maintains a Tuple containing:
1: the accummulated data.
2: state of the parser.
The aggregating function does an if-else to check if currently examined item is a group header or a regular item. Based on this, it updates the datastore (last part of the tuple) and/or changes the parser state (first part of the tuple).
In my case, the parser state is the currently active list (that upcoming items shall be inserted into).
var sequence = new[]{ "1","A","B","C","2","F","K","L","5","6","P","I","E"};
var aggr = Tuple.Create(new List<string>(), new Dictionary<int,List<string>>());
var res = sequence.Aggregate(aggr, (d, x) => {
int i;
if (Int32.TryParse(x, out i))
{
var newList = new List<string>();
d.Item2.Add(i,newList);
return Tuple.Create(newList,d.Item2);
} else
{
d.Item1.Add(x);
return d;
}
},d=>d.Item2);

Linq PredicateBuilder

public static IQueryable<SearchProfile> FilterData(string Filter, Repository<SearchProfileContext> dc)
{
IQueryable<SearchProfile> data = null;
var predicate = PredicateBuilder.True<SearchProfile>();
Filter = ExcludedParam(Filter);
if (!string.IsNullOrEmpty(Filter))`enter code here`
{
var stringToSplit = Filter;`enter code here`
List<string[]> arrays = new List<string[]>();
var primeArray = stringToSplit.Split('|');
for (int i = 0; i < primeArray.Length; i++)
{
string first = primeArray[i];
if (first.Contains("chkOrientation") == true)
{
string[] Array = first.Replace("chkOrientation=", "").Split(',');
predicate = predicate.And(a => Array.Contains(a.OrientaionID.ToString()));
}
if (first.Contains("chkProfession") == true)
{
string[] Array = first.Replace("chkProfession=", "").Split(',');
**predicate = predicate.And(a => Array.Contains(SqlFunctions.StringConvert((Double)a.ProfessionID)));**
}
}
data = dc.Select<SearchProfile>().Where(predicate).Distinct();
return data;
}
data = (from a in dc.Select<SearchProfile>().Where(a => a.PersonID > 0) select a).Distinct();
return data;
}
When I ran my program, I got this nasty error below:
LINQ to Entities does not recognize the method Int32 ToInteger(System.Object) method, and this method cannot be translated into a store expression.
then,I used SqlFunctions.StringConvert to make it work but the SQL LINQ generated was not evaluating. This is the sample output (it is comparing '1' and '2' instead of 1 and 2)**
Why are you casting a.ProfessionID to double? What type is a.ProfessionID of?
I think there is implicit conversion to integer, which causes calling the ToInteger method.
And why don't you convert items in Array to integer in the first place, and then use Array of ints in the query?

How can I stringify a BSON object inside of a MongoDB map function?

I have documents with field xyz containing
{ term: "puppies", page: { skip: 1, per_page: 20 } } // not useful as a composite key...
{ page: { skip: 1, per_page: 20 }, term: "puppies" } // different order, same contents
For the sake of determining the "top" values in xyz, I want to map them all to something like
emit('term="puppies",page={ skip: 1, per_page: 20 }', 1); // composite key
but I can't get the embedded objects into a meaningful strings:
emit('term="puppies",page=[object bson_object]', 1); // not useful
Any suggestions for a function to use instead of toString()?
# return the top <num> values of <field> based on a query <selector>
#
# example: top(10, :xyz, {}, {})
def top(num, field, selector, opts = {})
m = ::BSON::Code.new <<-EOS
function() {
var keys = [];
for (var key in this.#{field}) {
keys.push(key);
}
keys.sort ();
var sortedKeyValuePairs = [];
for (i in keys) {
var key = keys[i];
var value = this.#{field}[key];
if (value.constructor.name == 'String') {
var stringifiedValue = value;
} else if (value.constructor.name == 'bson_object') {
// this just says "[object bson_object]" which is not useful
var stringifiedValue = value.toString();
} else {
var stringifiedValue = value.toString();
}
sortedKeyValuePairs.push([key, stringifiedValue].join('='));
}
// hopefully we'll end up with something like
// emit("term=puppies,page={skip:1, per_page:20}")
// instead of
// emit("term=puppies,page=[object bson_object]")
emit(sortedKeyValuePairs.join(','), 1);
}
EOS
r = ::BSON::Code.new <<-EOS
function(k, vals) {
var sum=0;
for (var i in vals) sum += vals[i];
return sum;
}
EOS
docs = []
collection.map_reduce(m, r, opts.merge(:query => selector)).find({}, :limit => num, :sort => [['value', ::Mongo::DESCENDING]]).each do |doc|
docs.push doc
end
docs
end
Given that MongoDB uses SpiderMonkey as its internal JS engine, can't you use JSON.stringify (will work even if/when MongoDB switches to V8) or SpiderMonkey's non-standard toSource method?
(sorry, can't try it ATM to confirm it'd work)
toSource method will do the work, but it adds also brackets.
for a clean document use:
value.toSource().substring(1, value.toSource().length - 1)

Resources