Lodash sorting object by values, without losing the key - sorting

Let's say I have an object:
{Derp: 17, Herp: 2, Asd: 5, Foo: 8, Qwe: 12}
And I need to sort it by value. What I'm looking to get is:
{Derp: 17, Qwe: 12, Foo: 8, Asd: 5, Herp: 2}
I'd like to use lodash for it. When I use _.sortBy it doesn't retain the keys how ever:
_.sortBy({Derp: 17, Herp: 2, Asd: 5, Foo: 8, Qwe: 12}).reverse();
// [17, 12, 8, 5, 2]
Hell, I'd even settle for just the array of keys, but still sorted by the value in the input:
['Derp', 'Herp', 'Foo', 'Asd', 'Qwe']

This worked for me
o = _.fromPairs(_.sortBy(_.toPairs(o), 1).reverse())
Here's an example:
var o = {
a: 2,
c: 3,
b: 1
};
o = _.fromPairs(_.sortBy(_.toPairs(o), 1).reverse())
console.log(o);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>

I was struggling with a similar problem and I was able to solve it doing some transforms with lodash. For your problem it would be:
let doo = {Derp: 17, Herp: 2, Asd: 5, Foo: 8, Qwe: 12};
let foo = _.chain(doo)
.map((val, key) => {
return { name: key, count: val }
})
.sortBy('count')
.reverse()
.keyBy('name')
.mapValues('count')
.value();
console.log(foo);
// Derp: 17, Qwe: 12, Foo: 8, Asd: 5, Herp: 2 }

You could try like this,
_.mapValues(_.invert(_.invert(obj)),parseInt);
Object {Herp: 2, Asd: 5, Foo: 8, Qwe: 12, Derp: 17}
or
var obj = {Derp: 17, Herp: 2, Asd: 5, Foo: 8, Qwe: 12}
var result = _.reduceRight(_.invert(_.invert(obj)), function(current, val, key){
current[key] = parseInt(val);
return current;
},{});
Object {Derp: 17, Qwe: 12, Foo: 8, Asd: 5, Herp: 2}
or
Using Chain methods:
_.chain(obj).invert().invert().reduceRight(function(current, val, key){
current[key] = parseInt(val);
return current;
},{}).value()
Object {Derp: 17, Qwe: 12, Foo: 8, Asd: 5, Herp: 2}
Note: It depends on browser usally object properties order is not gurrantee in most case.

Vanilla ES6 version
const obj = {
a: 2,
c: 3,
b: 1
}
const sorted = Object.entries(obj)
.sort((a, b) => a[1] <= b[1] ? -1 : 1)
.reduce((acc, pair) => {
acc[pair[0]] = pair[1]
return acc
}, {})
// Output: { b: 1, a: 2, c: 3 }
This'll also let you access the sort callback, allowing for more granular sorting on deeper object properties.

Simple Vanila javascript approch
Object.fromEntries(Object.entries(obj).sort((a, b) => a[1] - b[1]));

Related

How to split range in groups?

I am writing microservice in Dart.
Service run workers by passing command args to workers like: "app.exe -a=1,2,3,4".
So if currentlyActiveWorkers is 4. Then jobs can be splited to sevaral workers like:
first: "app.exe -a=1,2,3,4"
second: "app.exe -a=5,6,7,8"
third: "app.exe -a=9,10,11,12"
fourth: "app.exe -a=13,14,15,16".
I wrote next prototype:
void main() {
int maxWorkers = 16;
int currentlyActiveWorkers = 2;
genJobs() {
int step = 1;
int sliceSize = (maxWorkers/currentlyActiveWorkers).round();
var list = [for(var i=step; i<=maxWorkers; i+=1) i];
for(int i in Iterable<int>.generate(currentlyActiveWorkers))
{
print(list.sublist(i * sliceSize, sliceSize * step));
step++;
}
}
genJobs();
}
It work fine if currentlyActiveWorkers is multiple of 2. It's generate suitable jobs numbers:
[1, 2, 3, 4, 5, 6, 7, 8]
[9, 10, 11, 12, 13, 14, 15, 16]
But there is bug if user specify for example 3. Last number 16 is loosing.
Output:
[1, 2, 3, 4, 5]
[6, 7, 8, 9, 10]
[11, 12, 13, 14, 15]
It does not matter for me the number element in every group +- 1 is ok for me.
Your rounding logic is ambiguous. Besides, you should handle the last chunk of data in different way:
void main() {
print(genJobs());
}
Map<int, List<int>> genJobs() {
final activeWorkers = 3;
final maxJobs = 16;
final jobs = List<int>.generate(maxJobs, (i) => i + 1);
final workerCapacity = (jobs.length / activeWorkers).floor();
var chunks = <int, List<int>>{};
for (var workerNumber = 0; workerNumber < activeWorkers; workerNumber++) {
final startIndex = workerNumber * workerCapacity;
final endIndex = startIndex + workerCapacity;
final chunk = jobs.sublist(
startIndex,
endIndex > jobs.length || workerNumber == activeWorkers - 1
? jobs.length
: endIndex,
);
chunks.addAll({workerNumber: chunk});
}
return chunks;
}
Your problem is that you are picking a fixed size for the slices first, even when the number of elements isn't a multiple of the slice count. You're lucky that it rounded down instead of up, otherwise you'd have gotten an index-out-of-range error (try your code with 17 elements and three groups).
First you should figure our what result you want. Then you can try coding that.
For something like 22 elements and four groups, you probably want two groups of 6 elements and two groups of 5 elements, not three groups of 6 and one of 4 (since you say +/-1 is OK, not +/- 2).
I would do something like:
/// Emits the integers from 0 to [elementCount] - 1 in [groupCount] grups.
///
/// The [elementCount] must be greater than zero.
/// The [groupCount] must be in the range 1..[elementCount],
/// meaning that each group will have at least one element, and
/// each element is in at least one group.
Iterable<List<int>> evenlySpreadGroups(int elementCount, int groupCount) sync* {
if (elementCount < 1) {
throw RangeError.range(elementCount, 1, null, "elementCount");
}
RangeError.checkValueInInterval(groupCount, 1, elementCount, "groupCount");
var list = <int>[];
var groupIndex = 1;
for (var i = 0; i < elementCount; i++) {
while (i * groupCount >= groupIndex * elementCount) {
yield list;
list = [];
groupIndex += 1;
}
list.add(i);
}
yield list;
}
(It's written to also work if you allow more groups than elements, any groupCount >= 1, you'll just get empty lists in the output which is just rarely useful).
The more solutions the better:
import 'package:lists/lists.dart';
void main() {
final maxPartSize1 = 5;
final data1 = genData(1, 16);
print('data: $data1');
final parts1 = split(data1, maxPartSize1);
print('parts by $maxPartSize1: $parts1');
print('=====');
final maxPartSize2 = 3;
final data2 = genData(2, 8);
print('data: $data2');
final parts2 = split(data2, maxPartSize2);
print('parts by $maxPartSize2: $parts2');
}
List<int> genData(int start, int length) {
return List<int>.generate(length, (i) => i + start);
}
Iterable<List<int>> split(List<int> data, int step) sync* {
final length = data.length;
for (final i in StepList(0, length - 1, step)) {
var i2 = i + step;
if (i2 > length) {
i2 = length;
}
yield data.sublist(i, i2);
}
}
Output:
data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
parts by 5: ([1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16])
=====
data: [2, 3, 4, 5, 6, 7, 8, 9]
parts by 3: ([2, 3, 4], [5, 6, 7], [8, 9])
Another way:
void main() {
final maxWorkers1 = 8;
final datLen1 = 16;
final data1 = [1, 2, 5, 6, 9, 10, 11, 12, 13, 14, 15, 16];
print('data: $data1');
final parts1 = split(data1, maxWorkers1);
print('$maxWorkers1 workers: $parts1');
print('=====');
final maxWorkers2 = 4;
final datLen2 = 11;
final data2 = genData(3, datLen2);
print('data: $data2');
final parts2 = split(data2, maxWorkers2);
print('$maxWorkers2 workers: $parts2');
print('=====');
final maxWorkers3 = 8;
final data3 = [7, 8, 3, 4];
print('data: $data3');
final parts3 = split(data3, maxWorkers3);
print('$maxWorkers3 workers: $parts3');
}
List<int> genData(int start, int length) {
return List<int>.generate(length, (i) => i + start);
}
Iterable<List<int>> split(List<int> data, int divider) sync* {
if (divider <= 0) {
throw RangeError.value(divider, 'divider', 'Must be greater than 0');
}
if (data.isEmpty) {
// Nothing to do
return;
}
final length = data.length;
divider = divider > length ? length : divider;
var partSize = length ~/ divider;
if (length != partSize * divider) {
partSize++;
}
for (var i = 0; i < divider; i++) {
final start = i * partSize;
if (start >= length) {
break;
}
var end = start + partSize;
if (end > length) {
end = length;
}
yield data.sublist(start, end);
}
}
Output:
data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
3 workers: ([1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12], [13, 14, 15, 16])
=====
data: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
4 workers: ([3, 4, 5], [6, 7, 8], [9, 10, 11], [12, 13])
=====
data: [7, 8, 3, 4]
8 workers: ([7], [8], [3], [4])

What is the best approach to solve this problem?

If an array contained [1, 10, 3, 5, 2, 7] and k = 2, combine the set as {110, 35, 27}, sort the set {27, 35, 110} and split the set into array as [2, 7, 3, 5, 1, 10]
Here is a way to implement this in JavaScript:
const k = 2;
const arr = [1, 10, 3, 5, 2, 7];
// STEP 1 - Combine the set by k pair number
const setCombined = []
for(let i = 0; i < arr.length; ++i) {
if(i % k === 0) {
setCombined.push(parseInt(arr.slice(i, i + k ).join('')))
}
}
console.log('STEP1 - combined: \n', setCombined);
// STEP 2 - Sort
const sortedArray = setCombined.sort((a, b) => a - b)
console.log('STEP2 - sorted: \n', sortedArray);
// STEP 3 - Split sorted
const splitArray = sortedArray.join('').split('').map(e => parseInt(e))
console.log('STEP3 - split: \n', splitArray);
I was not sure though when you said to combine set, if you really ment to keep only unique values or not... Let me know

Is it possible to calculate the Chinese Zodiac, or must I use a lookup table?

I'm building an application that will tell your Chinese sign. I looked around but only found charts (from 1900 to 2020), and no logic to create something more dynamic.
Is there no logic for determining a Chinese zodiac?
Does that answer your question:
public string ChineseZodiac(System.DateTime date)
{
System.Globalization.EastAsianLunisolarCalendar cc =
new System.Globalization.ChineseLunisolarCalendar();
int sexagenaryYear = cc.GetSexagenaryYear(date);
int terrestrialBranch = cc.GetTerrestrialBranch(sexagenaryYear);
// string[] years = "rat,ox,tiger,hare,dragon,snake,horse,sheep,monkey,fowl,dog,pig".Split(',');
// string[] years = "Rat,Ox,Tiger,Rabbit,Dragon,Snake,Horse,Goat,Monkey,Rooster,Dog,Pig".Split(',');
// string[] years = new string[]{ "rat", "ox", "tiger", "hare", "dragon", "snake", "horse", "sheep", "monkey", "fowl", "dog", "pig" };
string[] years = new string[]{ "Rat", "Ox", "Tiger", "Rabbit", "Dragon", "Snake", "Horse", "Goat", "Monkey", "Rooster", "Dog", "Pig" };
return years[terrestrialBranch - 1];
} // End Function ChineseZodiac
Fore those that need the source code for another programming language:
I've ripped the corresponding sources out of .NET Framwork, here:
https://gist.github.com/ststeiger/709354299a457e2d79b06d0127096fee
Edit:
I ported this to JavaScript:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChineseZodiac = void 0;
var DateTimeKind;
(function (DateTimeKind) {
DateTimeKind[DateTimeKind["Unspecified"] = 0] = "Unspecified";
DateTimeKind[DateTimeKind["Utc"] = 1] = "Utc";
DateTimeKind[DateTimeKind["Local"] = 2] = "Local";
})(DateTimeKind || (DateTimeKind = {}));
var GregorianCalendar = (function () {
function GregorianCalendar() {
}
GregorianCalendar.prototype.GetYear = function (time) {
var ticks = time.JavaScriptTicks;
var dt = new Date(ticks);
return dt.getUTCFullYear();
};
GregorianCalendar.prototype.GetMonth = function (time) {
var ticks = time.JavaScriptTicks;
var dt = new Date(ticks);
return (dt.getUTCMonth() + 1);
};
GregorianCalendar.prototype.GetDayOfMonth = function (time) {
var ticks = time.JavaScriptTicks;
var dt = new Date(ticks);
return dt.getUTCDate();
};
return GregorianCalendar;
}());
var DotNetDateTime = (function () {
function DotNetDateTime(ticks, dt) {
this.m_ticks = 0;
this.m_ticks = ticks;
}
DotNetDateTime.fromJsDate = function (dt) {
var jsTicks = dt.getTime();
var dotNetJsbaseTicks = 621355968000000000;
var tenK = 10000;
var dotTicks = dotNetJsbaseTicks + jsTicks * tenK;
return new DotNetDateTime(dotTicks, DateTimeKind.Unspecified);
};
Object.defineProperty(DotNetDateTime.prototype, "Ticks", {
get: function () {
return this.m_ticks;
},
enumerable: false,
configurable: true
});
Object.defineProperty(DotNetDateTime.prototype, "JavaScriptTicks", {
get: function () {
var dotNetJsbaseTicks = 621355968000000000;
var dotNetTicksSince1970 = this.m_ticks - dotNetJsbaseTicks;
var jsTicks = parseInt((dotNetTicksSince1970 / 10000).toString(), 10);
return jsTicks;
},
enumerable: false,
configurable: true
});
return DotNetDateTime;
}());
var MinLunisolarYear = 1901;
var MaxLunisolarYear = 2100;
var DaysPerYear = 365;
var DaysPer4Years = DaysPerYear * 4 + 1;
var DaysPer100Years = DaysPer4Years * 25 - 1;
var DaysPer400Years = DaysPer100Years * 4 + 1;
var DaysTo10000 = DaysPer400Years * 25 - 366;
var TicksPerMillisecond = 10000;
var TicksPerSecond = TicksPerMillisecond * 1000;
var TicksPerMinute = TicksPerSecond * 60;
var TicksPerHour = TicksPerMinute * 60;
var TicksPerDay = TicksPerHour * 24;
var MinTicks = 0;
var MaxTicks = DaysTo10000 * TicksPerDay - 1;
var MinValue = new DotNetDateTime(MinTicks, DateTimeKind.Unspecified);
var MaxValue = new DotNetDateTime(MaxTicks, DateTimeKind.Unspecified);
var Jan1Month = 1;
var Jan1Date = 2;
var nDaysPerMonth = 3;
var s_daysToMonth365 = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
var s_daysToMonth366 = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335];
var s_yinfo = [
[0, 2, 19, 19168],
[0, 2, 8, 42352],
[5, 1, 29, 21096],
[0, 2, 16, 53856],
[0, 2, 4, 55632],
[4, 1, 25, 27304],
[0, 2, 13, 22176],
[0, 2, 2, 39632],
[2, 1, 22, 19176],
[0, 2, 10, 19168],
[6, 1, 30, 42200],
[0, 2, 18, 42192],
[0, 2, 6, 53840],
[5, 1, 26, 54568],
[0, 2, 14, 46400],
[0, 2, 3, 54944],
[2, 1, 23, 38608],
[0, 2, 11, 38320],
[7, 2, 1, 18872],
[0, 2, 20, 18800],
[0, 2, 8, 42160],
[5, 1, 28, 45656],
[0, 2, 16, 27216],
[0, 2, 5, 27968],
[4, 1, 24, 44456],
[0, 2, 13, 11104],
[0, 2, 2, 38256],
[2, 1, 23, 18808],
[0, 2, 10, 18800],
[6, 1, 30, 25776],
[0, 2, 17, 54432],
[0, 2, 6, 59984],
[5, 1, 26, 27976],
[0, 2, 14, 23248],
[0, 2, 4, 11104],
[3, 1, 24, 37744],
[0, 2, 11, 37600],
[7, 1, 31, 51560],
[0, 2, 19, 51536],
[0, 2, 8, 54432],
[6, 1, 27, 55888],
[0, 2, 15, 46416],
[0, 2, 5, 22176],
[4, 1, 25, 43736],
[0, 2, 13, 9680],
[0, 2, 2, 37584],
[2, 1, 22, 51544],
[0, 2, 10, 43344],
[7, 1, 29, 46248],
[0, 2, 17, 27808],
[0, 2, 6, 46416],
[5, 1, 27, 21928],
[0, 2, 14, 19872],
[0, 2, 3, 42416],
[3, 1, 24, 21176],
[0, 2, 12, 21168],
[8, 1, 31, 43344],
[0, 2, 18, 59728],
[0, 2, 8, 27296],
[6, 1, 28, 44368],
[0, 2, 15, 43856],
[0, 2, 5, 19296],
[4, 1, 25, 42352],
[0, 2, 13, 42352],
[0, 2, 2, 21088],
[3, 1, 21, 59696],
[0, 2, 9, 55632],
[7, 1, 30, 23208],
[0, 2, 17, 22176],
[0, 2, 6, 38608],
[5, 1, 27, 19176],
[0, 2, 15, 19152],
[0, 2, 3, 42192],
[4, 1, 23, 53864],
[0, 2, 11, 53840],
[8, 1, 31, 54568],
[0, 2, 18, 46400],
[0, 2, 7, 46752],
[6, 1, 28, 38608],
[0, 2, 16, 38320],
[0, 2, 5, 18864],
[4, 1, 25, 42168],
[0, 2, 13, 42160],
[10, 2, 2, 45656],
[0, 2, 20, 27216],
[0, 2, 9, 27968],
[6, 1, 29, 44448],
[0, 2, 17, 43872],
[0, 2, 6, 38256],
[5, 1, 27, 18808],
[0, 2, 15, 18800],
[0, 2, 4, 25776],
[3, 1, 23, 27216],
[0, 2, 10, 59984],
[8, 1, 31, 27432],
[0, 2, 19, 23232],
[0, 2, 7, 43872],
[5, 1, 28, 37736],
[0, 2, 16, 37600],
[0, 2, 5, 51552],
[4, 1, 24, 54440],
[0, 2, 12, 54432],
[0, 2, 1, 55888],
[2, 1, 22, 23208],
[0, 2, 9, 22176],
[7, 1, 29, 43736],
[0, 2, 18, 9680],
[0, 2, 7, 37584],
[5, 1, 26, 51544],
[0, 2, 14, 43344],
[0, 2, 3, 46240],
[4, 1, 23, 46416],
[0, 2, 10, 44368],
[9, 1, 31, 21928],
[0, 2, 19, 19360],
[0, 2, 8, 42416],
[6, 1, 28, 21176],
[0, 2, 16, 21168],
[0, 2, 5, 43312],
[4, 1, 25, 29864],
[0, 2, 12, 27296],
[0, 2, 1, 44368],
[2, 1, 22, 19880],
[0, 2, 10, 19296],
[6, 1, 29, 42352],
[0, 2, 17, 42208],
[0, 2, 6, 53856],
[5, 1, 26, 59696],
[0, 2, 13, 54576],
[0, 2, 3, 23200],
[3, 1, 23, 27472],
[0, 2, 11, 38608],
[11, 1, 31, 19176],
[0, 2, 19, 19152],
[0, 2, 8, 42192],
[6, 1, 28, 53848],
[0, 2, 15, 53840],
[0, 2, 4, 54560],
[5, 1, 24, 55968],
[0, 2, 12, 46496],
[0, 2, 1, 22224],
[2, 1, 22, 19160],
[0, 2, 10, 18864],
[7, 1, 30, 42168],
[0, 2, 17, 42160],
[0, 2, 6, 43600],
[5, 1, 26, 46376],
[0, 2, 14, 27936],
[0, 2, 2, 44448],
[3, 1, 23, 21936],
[0, 2, 11, 37744],
[8, 2, 1, 18808],
[0, 2, 19, 18800],
[0, 2, 8, 25776],
[6, 1, 28, 27216],
[0, 2, 15, 59984],
[0, 2, 4, 27296],
[4, 1, 24, 43872],
[0, 2, 12, 43744],
[0, 2, 2, 37600],
[3, 1, 21, 51568],
[0, 2, 9, 51552],
[7, 1, 29, 54440],
[0, 2, 17, 54432],
[0, 2, 5, 55888],
[5, 1, 26, 23208],
[0, 2, 14, 22176],
[0, 2, 3, 42704],
[4, 1, 23, 21224],
[0, 2, 11, 21200],
[8, 1, 31, 43352],
[0, 2, 19, 43344],
[0, 2, 7, 46240],
[6, 1, 27, 46416],
[0, 2, 15, 44368],
[0, 2, 5, 21920],
[4, 1, 24, 42448],
[0, 2, 12, 42416],
[0, 2, 2, 21168],
[3, 1, 22, 43320],
[0, 2, 9, 26928],
[7, 1, 29, 29336],
[0, 2, 17, 27296],
[0, 2, 6, 44368],
[5, 1, 26, 19880],
[0, 2, 14, 19296],
[0, 2, 3, 42352],
[4, 1, 24, 21104],
[0, 2, 10, 53600],
[8, 1, 30, 59696],
[0, 2, 18, 54560],
[0, 2, 7, 55968],
[6, 1, 27, 27472],
[0, 2, 15, 22224],
[0, 2, 5, 19168],
[4, 1, 25, 42216],
[0, 2, 12, 41680],
[0, 2, 1, 53584],
[2, 1, 21, 55592],
[0, 2, 9, 54560],
];
function GregorianIsLeapYear(y) {
if ((y % 4) != 0) {
return false;
}
if ((y % 100) != 0) {
return true;
}
return (y % 400) == 0;
}
function GetYearInfo(lunarYear, index) {
if (lunarYear < MinLunisolarYear || lunarYear > MaxLunisolarYear) {
throw new Error("year");
}
return s_yinfo[lunarYear - MinLunisolarYear][index];
}
function CheckTicksRange(ticks) {
if (ticks < MinValue.Ticks || ticks > MaxValue.Ticks) {
throw new Error("time");
}
}
function GregorianToLunar(solarYear, solarMonth, solarDate) {
var outData = { lunarYear: 0, lunarMonth: 0, lunarDate: 0 };
var isLeapYear = GregorianIsLeapYear(solarYear);
var jan1Month;
var jan1Date;
var solarDay = isLeapYear ? s_daysToMonth366[solarMonth - 1] : s_daysToMonth365[solarMonth - 1];
solarDay += solarDate;
var lunarDay = solarDay;
outData.lunarYear = solarYear;
if (outData.lunarYear == (MaxLunisolarYear + 1)) {
outData.lunarYear--;
lunarDay += (GregorianIsLeapYear(outData.lunarYear) ? 366 : 365);
jan1Month = GetYearInfo(outData.lunarYear, Jan1Month);
jan1Date = GetYearInfo(outData.lunarYear, Jan1Date);
}
else {
jan1Month = GetYearInfo(outData.lunarYear, Jan1Month);
jan1Date = GetYearInfo(outData.lunarYear, Jan1Date);
if ((solarMonth < jan1Month) ||
(solarMonth == jan1Month && solarDate < jan1Date)) {
outData.lunarYear--;
lunarDay += (GregorianIsLeapYear(outData.lunarYear) ? 366 : 365);
jan1Month = GetYearInfo(outData.lunarYear, Jan1Month);
jan1Date = GetYearInfo(outData.lunarYear, Jan1Date);
}
}
lunarDay -= s_daysToMonth365[jan1Month - 1];
lunarDay -= (jan1Date - 1);
var mask = 0x8000;
var yearInfo = GetYearInfo(outData.lunarYear, nDaysPerMonth);
var days = ((yearInfo & mask) != 0) ? 30 : 29;
outData.lunarMonth = 1;
while (lunarDay > days) {
lunarDay -= days;
outData.lunarMonth++;
mask >>= 1;
days = ((yearInfo & mask) != 0) ? 30 : 29;
}
outData.lunarDate = lunarDay;
return outData;
}
function TimeToLunar(time) {
var gregorianCalendar = new GregorianCalendar();
var gy = gregorianCalendar.GetYear(time);
var gm = gregorianCalendar.GetMonth(time);
var gd = gregorianCalendar.GetDayOfMonth(time);
var ad = GregorianToLunar(gy, gm, gd);
return {
year: ad.lunarYear
};
}
function GetSexagenaryYear(time) {
CheckTicksRange(time.Ticks);
var x = TimeToLunar(time);
return ((x.year - 4) % 60) + 1;
}
function GetTerrestrialBranch(sexagenaryYear) {
if (sexagenaryYear < 1 || sexagenaryYear > 60) {
throw new Error("sexagenaryYear");
}
return ((sexagenaryYear - 1) % 12) + 1;
}
function ChineseZodiac(date) {
var dotNetDate = DotNetDateTime.fromJsDate(date);
var sexagenaryYear = GetSexagenaryYear(dotNetDate);
var terrestrialBranch = GetTerrestrialBranch(sexagenaryYear);
var years = ["Rat", "Ox", "Tiger", "Rabbit", "Dragon", "Snake", "Horse", "Goat", "Monkey", "Rooster", "Dog", "Pig"];
return years[terrestrialBranch - 1];
}
exports.ChineseZodiac = ChineseZodiac;
Testing:
var exports = {};
// [copy-pasting above code]
exports.ChineseZodiac(new Date(1970,0,1))
exports.ChineseZodiac(new Date(2021,0,1))
exports.ChineseZodiac(new Date(2022,0,1))
all returns the same values as dotnet.
By using a calculator
2013-4=2009
2009/12= 167.41666
167*12=2004
2009-2004=5 (5 is snake which was the animal for 2013)
Use this to calculate the year.
<?php
$year = 2013;
switch (($year - 4) % 12) {
case 0: $zodiac = 'Rat'; break;
case 1: $zodiac = 'Ox'; break;
case 2: $zodiac = 'Tiger'; break;
case 3: $zodiac = 'Rabbit'; break;
case 4: $zodiac = 'Dragon'; break;
case 5: $zodiac = 'Snake'; break;
case 6: $zodiac = 'Horse'; break;
case 7: $zodiac = 'Goat'; break;
case 8: $zodiac = 'Monkey'; break;
case 9: $zodiac = 'Rooster'; break;
case 10: $zodiac = 'Dog'; break;
case 11: $zodiac = 'Pig'; break;
}
echo "{$year} is the year of the {$zodiac}.<br />";
?>
Wikipedia has a reference to 2044.
http://en.wikipedia.org/wiki/Chinese_zodiac
Using Year of the Rat as an example (for years after 1984), it looks like Rat cycles every:
383, 353, 353, 383, 354 days
Notice the last cycle is 354 which is more than likely due to Leap Year. Maybe using this formula, you can work out any year up to maybe 2100 or so.
I used the following T-SQL to deduce those numbers
select DATEDIFF(D,'02/2/1984', '02/19/1985')
select DATEDIFF(D,'02/19/1996', '02/6/1997')
select DATEDIFF(D,'02/7/2008', '01/25/2009')
select DATEDIFF(D,'01/25/2020', '02/11/2021')
select DATEDIFF(D,'02/11/2032', '01/30/2033')
If you are serious about finding a non-tabular mechanism for calculating the years of the Chinese Zodiac, then I recommend looking at 'Calendrical Calculations, 3rd Edition' which has (LISP) code to handle calculations for the Chinese New Year, and from that, deducing the Year of the <relevant-animal> is straight-forward. That book covers many calendrical systems and is an interesting read. Being a luni-solar calendar, the Chinese calendar is quite complex; the mathematics gets quite detailed.
It is probably simpler, and likely more compact, code-wise, to use a table, though.
There is always a question what is quicker to test and verify.
When developing chinese zodiac calculator on calculla, we decided to use lookup table - as this was just quicker and more convenient to code, than actually testing any algo for it (even if algo may be simple, you still need time to test it).
This lookup was not a big table and you can actually get the javascript code from source of our website.
First of all you must create an array of the zodiac signs exactly as below
DECLARE sign : [array(1.....12)]
DECLARE year : INTEGER
sign(1) ← "Rat"
sign(2) ← "Ox"
sign(3) ← "Tiger"
sign(4) ← "Rabbit"
sign(5) ← "Dragon"
sign(6) ← "Snake"
sign(7) ← "Horse"
sign(8) ← "Goat"
sign(9) ← "Monkey"
sign(10) ← "Rooster"
sign(11) ← "Dog"
sign(12) ← "Pig"
DECLARE X, Y, N : INTEGER
X ← (year - 4)
Y ← (X DIV 12) // DIV return only the natural number
Y ← (Y * 12)
N ← N + 1
OUTPUT sign(N)
Public Function ChineseSign(ByVal DoB As Date) As String
Dim CSign As String = ""
Dim YearSign As New Short
YearSign = Year(DoB) Mod 12
'// Uncomment the following to use Feb 12th as New Year calculation //
'If Month(DoB) = 1 Then
'YearSign = YearSign - 1
'If YearSign = 0 Then YearSign = 12
'ElseIf Month(DoB) = 2 And DoB.Day < 12 Then
'YearSign = YearSign - 1
'If YearSign = 0 Then YearSign = 12
'End If
Select Case YearSign
Case 1
CSign = "Rooster"
Case 2
CSign = "Dog"
Case 3
CSign = "Pig (Boar)"
Case 4
CSign = "Rat"
Case 5
CSign = "Ox"
Case 6
CSign = "Tiger"
Case 7
CSign = "Rabbit"
Case 8
CSign = "Dragon"
Case 9
CSign = "Snake"
Case 10
CSign = "Horse"
Case 11
CSign = "Goat"
Case 12
CSign = "Monkey"
End Select
Select Case CSign
Case "Ox"
Return "an " & CSign
Case Else
Return "a " & CSign
End Select
End Function

How to simplify generation of number range using linq?

If I have a number x which can change, say its now 25... how do I do the below in a simpler way?
colQty.DataSource = new List<Int16> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 };
Something like
var range = Enumerable.Range(start: 1, count: x).ToList();
And you could use ToList<Int16>() but I wouldn't.
Use this:
var numbers = Enumerable.Range(1, 25);
This will create an IEnumerable<int> with the numbers 1 to 25. If you need a List<int> instead, add .ToList() after Range:
var numbers = Enumerable.Range(1, 25).ToList();
var list = new List<int>();
for(var i = 1; i <= x; i++){ list.Add(i);}
colQty.DataSource = list;

Using LINQ to filter rows of a matrix based on inclusion in an array

I have a matrix, IEnumerable<IEnumerable<int>> matrix, for example:
{ {10,23,16,20,2,4}, {22,13,1,33,21,11 }, {7,19,31,12,6,22}, ... }
and another array:
int[] arr={ 10, 23, 16, 20}
I want to filter the matrix on the condition that I group all rows of the matrix which contain the same number of elements from arr.
That is to say the first row in the matrix {10,23,16,20,2,4} has 4 numbers from arr, this array should be grouped with the rest of the rows with 4 numbers from arr.
better to use linq, thank you very much!
This worked for me:
private static void Main(string[] args)
{
int[] searchNums = new int[] {10, 23, 16, 20};
var groupByCount = from o in lists
group o by o.Count(num => searchNums.Contains(num)) into g
orderby g.Key
select g;
foreach(var grouping in groupByCount)
{
int countSearchNums = grouping.Key;
Console.WriteLine("Lists that have " + countSearchNums + " of the numbers:");
foreach(IEnumerable<int> list in grouping)
{
Console.WriteLine("{{ {0} }}", String.Join(", ", list.Select(o => o.ToString()).ToArray()));
}
}
Console.ReadKey();
}
private static List<List<int>> lists = new List<List<int>>
{
new List<int> {10, 23, 16, 20, 2, 4},
new List<int> {22, 13, 1, 33, 21, 11},
new List<int> {7, 19, 31, 12, 6, 22},
new List<int> {10, 13, 31, 12, 6, 22},
new List<int> {10, 19, 20, 16, 6, 22},
new List<int> {10},
new List<int> {10, 13, 16, 20},
};
Output:
Lists that have 0 of the numbers:
{ 22, 13, 1, 33, 21, 11 }
{ 7, 19, 31, 12, 6, 22 }
Lists that have 1 of the numbers:
{ 10, 13, 31, 12, 6, 22 }
{ 10 }
Lists that have 3 of the numbers:
{ 10, 19, 20, 16, 6, 22 }
{ 10, 13, 16, 20 }
Lists that have 4 of the numbers:
{ 10, 23, 16, 20, 2, 4 }

Resources