I got an table like this:
tbl = {
['etc1'] = 1337,
['etc2'] = 14477,
['etc3'] = 1336,
['etc4'] = 1335
}
And now I need to sort this table to get output from highes to lowest value:
tbl = {
['etc2'] = 14477,
['etc1'] = 1337,
['etc3'] = 1336,
['etc4'] = 1335
}
Already tried lots of functions like table.sort or others from the official manual, but nothing helped. So hope you'll help me out guys!
Regards.
Lua tables do not have ordering other than by their keys. You will need to structure your data more like this:
tbl = {
[1] = { ['etc2'] = 14477 },
[2] = { ['etc1'] = 1337 },
[3] = { ['etc3'] = 1336 },
[4] = { ['etc4'] = 1335 }
}
or this:
tbl = {
[1] = { 'etc2', 14477 },
[2] = { 'etc1', 1337 },
[3] = { 'etc3', 1336 },
[4] = { 'etc4', 1335 }
}
or this, if you want to use it in conjunction with the original table:
tbl_keys = {
[1] = 'etc2',
[2] = 'etc1',
[3] = 'etc3',
[4] = 'etc4'
}
Note that I was very explicit and wrote all the numeric indices. You can of course omit them, so the last solution would be:
tbl_keys = {
'etc2',
'etc1',
'etc3',
'etc4'
}
Maybe this means you should write a function which turns the original data into this new form, or maybe you can get it done earlier on, before the table is made in the first place.
Related
I have data
{
"id": 1000,
"price": "99,01USA",
},
{
"id": 1001,
"price": "100USA",
},
{
"id": 1002,
"price": "780USA",
},
{
"id": 1003,
"price": "20USA",
},
How I sort order by price (ASC , DESC)
You can alter it a little to parse price to integer and then sort it
You can create a dynamic sort function that sorts objects by their value that you pass:
function dynamicSort(property) {
var sortOrder = 1;
if(property[0] === "-") {
sortOrder = -1;
property = property.substr(1);
}
return function (a,b) {
/* next line works with strings and numbers,
* and you may want to customize it to your needs
*/
var result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0;
return result * sortOrder;
}
}
So you can have an array of objects like this:
var People = [
{Name: "Name", Surname: "Surname"},
{Name:"AAA", Surname:"ZZZ"},
{Name: "Name", Surname: "AAA"}
];
...and it will work when you do:
People.sort(dynamicSort("Name"));
People.sort(dynamicSort("Surname"));
People.sort(dynamicSort("-Surname"));
Actually this already answers the question. Below part is written because many people contacted me, complaining that it doesn't work with multiple parameters.
Multiple Parameters
You can use the function below to generate sort functions with multiple sort parameters.
function dynamicSortMultiple() {
/*
* save the arguments object as it will be overwritten
* note that arguments object is an array-like object
* consisting of the names of the properties to sort by
*/
var props = arguments;
return function (obj1, obj2) {
var i = 0, result = 0, numberOfProperties = props.length;
/* try getting a different result from 0 (equal)
* as long as we have extra properties to compare
*/
while(result === 0 && i < numberOfProperties) {
result = dynamicSort(props[i])(obj1, obj2);
i++;
}
return result;
}
}
Which would enable you to do something like this:
People.sort(dynamicSortMultiple("Name", "-Surname"));
Subclassing Array
For the lucky among us who can use ES6, which allows extending the native objects:
class MyArray extends Array {
sortBy(...args) {
return this.sort(dynamicSortMultiple(...args));
}
}
That would enable this:
MyArray.from(People).sortBy("Name", "-Surname");
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")
I know that Filter and contains in Swift are very powerful and it has made many of my works easy. However, I'm stranded in a situation where I have to filter/verify a set of different values from an array of Model.
Let's assume I have a model like below
struct Person {
var name: String
var ID: String
}
And I have an array of models like below:
[
{
sku = "123"
attributes = [
{
name:"Victor"
ID:"250"
},
{
name:"Shaw"
ID:"252"
}
]
},
{
sku = "123"
attributes = [
{
name:"John"
ID:"222"
},
{
name:"Nixon"
ID:"333"
}
]
}
]
Now I would like to filter the sku by multiple IDs inside attributes . For example, if I need to filter skus which has ID:250 and ID:252 I need something like this to achieve it.
personModelArray.forEach { person in
person.attributes.contains(where: { $0.ID == 250 && $0.ID == 252 })
}
OR
personModelArray.forEach { person in
person.attributes.filter { $0.ID == 250 && $0.ID == 252 }
}
What I want is a condition like to to verify that the attributes array has both the values.
The condition $0.ID == 250 && $0.ID == 252 is ALWAYS false! How one storage could have two different values at the same moment?
Look at this simple example
struct Item {
let a: Int
let b: String
}
let arr = [Item(a: 0, b: "zero"),
Item(a: 1, b: "one"),
Item(a: 2, b: "two"),
Item(a: 4, b: "four")]
To filter all items, where a == 1 OR a == 4
let arr0 = arr.filter { item in
item.a == 1 ||
item.a == 4
}
or, if You prefer $ parameter notation
let arr1 = arr.filter { $0.a == 1 || $0.a == 4 }
eventually,
let arr2 = arr.filter { item in
[1, 4].contains(item.a)
}
let arr3 = arr.filter { [1, 4].contains($0.a) }
I have list of objects as described below:
List<Maths> mObjs = new List<Maths>();
mObjs.Add(new Maths{ Name = "Jack", M1 = 10, M2 = 5, M3 = 0, M4 = 2, M5 =1 });
mObjs.Add(new Maths { Name = "Jill", M1 = 2, M2 = 3, M3 = 4, M4 = 1, M5 = 0 });
mObjs.Add(new Maths { Name = "Michel", M1 = 12, M2 = 15, M3 = 10, M4 = 12, M5 = 11 });
Now I need to calculated the total aggregated value for all three people.
I need to get the below results, probably a new other class
List<Results> mRes = new List<Results>();
public class Results{
public string Name { get; set; }
public int TotalValue { get; set; }
}
mRes.Name = "M1"
mRes.TotalValue = 24;
mRes.Name = "M2"
mRes.TotalValue = 23;
mRes.Name = "M3"
mRes.TotalValue = 14;
mRes.Name = "M4"
mRes.TotalValue = 15;
mRes.Name = "M5"
mRes.TotalValue = 12;
How can I get this data from mObjs using linq query? I know we can do it using for, but want to know if there are any better ways to get this using linq query because that reduces lines of code and I have similar requirements in many other places and dont want to write number of foreach or fors every time.
You can use a pre selection list to list both the name and the field to select
var lookups = new Dictionary<string,Func<Maths,int>> {
{"M1", x => x.M1 },
{"M2", x => x.M2 },
{"M3", x => x.M3 },
{"M4", x => x.M4 },
{"M5", x => x.M5 },
};
Then you can simply do
var mRes = dlookups.Select(x => new Results {
Name= x.Key,
TotalValue = mObjs.Sum(x.Value)
}).ToList();
BEGIN UPDATED*
In response to comments
The lambda expression is just a function from your source class to an int.
For example
class Sub1 {
string M3 {get;set;}
int M4 {get;set;}
}
class Math2 {
string Name {get;set;}
string M1 {get;set;}
string M2 {get;set;}
Sub1 Sub {get;set;}
}
var lookups = new Dictionary<string,Func<Math2,int>> {
{ "M1", x => int.Parse(x.M1) },
{ "M2", x => int.Parse(x.M2) },
{ "M3", x => int.Parse(x.Sub.M3) },
{ "M4", x => int.Parse(x.Sub.M4} }
};
Or if you want to put a little error checking in, you can either use functions or embed the code.
int GetInt(string source) {
if (source == null) return 0;
int result;
return int.TryParse(source, out result) ? result : 0;
}
var lookups = new Dictionary<string,Func<Math2,int>> {
{ "M1", x => {
int result;
return x == null ? 0 : (int.TryParse(x,out result) ? result : 0);
},
{ "M2", x => GetInt(x) },
{ "M3", x => x.Sub == null ? 0 : GetInt(x.Sub.M3) },
{ "M4", x => x.Sub == null ? 0 : x.Sub.M4}
};
END UPDATED
If you want to go further you could use reflection to build the lookups dictionary.
Here is a helper function that will generate the lookups for all Integer properties of a class.
public Dictionary<string,Func<T,int>> GenerateLookups<T>() where T: class {
// This just looks for int properties, you could add your own filter
var properties = typeof(T).GetProperties().Where(pi => pi.PropertyType == typeof(int));
var parameter = Expression.Parameter(typeof(T));
return properties.Select(x => new {
Key = x.Name,
Value = Expression.Lambda<Func<T,int>>(Expression.Property(parameter,x),parameter).Compile()
}).ToDictionary (x => x.Key, x => x.Value);
}
Now you can just do:
var mRes=GenerateLookups<Maths>().Select( x => new Results
{
Name = x.Key,
TotalValue = mObjs.Sum(x.Value)
}).ToList();
Not very smart but efficient and readable:
int m1Total= 0;
int m2Total= 0;
int m3Total= 0;
int m4Total= 0;
int m5Total= 0;
foreach(Maths m in mObjs)
{
m1Total += m.M1;
m2Total += m.M2;
m3Total += m.M3;
m4Total += m.M4;
m5Total += m.M5;
}
List<Results> mRes = new List<Results>
{
new Results{ Name = "M1", TotalValue = m1Total },
new Results{ Name = "M2", TotalValue = m2Total },
new Results{ Name = "M3", TotalValue = m3Total },
new Results{ Name = "M4", TotalValue = m4Total },
new Results{ Name = "M5", TotalValue = m5Total },
};
Result:
Name: "M1" TotalValue: 24
Name: "M2" TotalValue: 23
Name: "M3" TotalValue: 14
Name: "M4" TotalValue: 15
Name: "M5" TotalValue: 12
Edit: since you've explicitly asked for LINQ, if the properties are always these five i don't see why you need to use LINQ at all. If the number can change i would use a different structure.
You could for example use
a single List<Measurement> instead of multiple properties where Measurement is another class that stores the name and the value or you could use
a Dictionary<string, int> for efficient lookup.
You can try out some thing like this :
mRes.Add(new Results() { Name = "M1", TotalValue = mObjs.Sum(x => x.M1) });
To programmatically iterate through all the class properties, you might need to employ reflection.
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)