How to expose objects to a given pattern? - javascript

I automatically generate an array of objects in a random order. This means that the initial order of objects can be different.
var a = [{account: true},{name: true},{amount: true},{address: true}];
or
var a = [{name: true},{account: true},{amount: true},{address: true}];
How to expose objects to a given pattern?
var a = [{amount: true},{address: true},{account: true},{name: true}];
How can I arrange the automatically generated elements in a different order each time in a special order? I need the items to be in order - amount, address, account, name.
AND I have the solution!
var order = [{account: true},{name: true},{amount: true},{address: true}];
var pattern = [{amount: true},{address: true},{account: true},{name: true}];
var sort = pattern.map(pattern => order.find(order => order[0] === pattern[0]));
console.log(sort); //[{amount: true},{address: true},{account: true},{name: true}]

I'm not able to get a correct sort using your code. It seems to return an array with all the same elements of whatever is the first element of the order array.
[{ account: true },{ account: true },{ account: true },{ account: true }]
const order = [
{ account: true },
{ name: true },
{ amount: true },
{ address: true }
];
const pattern = [
{ amount: true },
{ address: true },
{ account: true },
{ name: true }
];
var sort = pattern.map((pattern) =>
order.find((order) => order[0] === pattern[0])
);
console.log("unsorted: ", JSON.stringify(order));
console.log("sorted: ", JSON.stringify(sort));
Here's a sort that does appear to sort your array element objects.
const sortedData = data.sort(
(a, b) =>
!!b.amount - !!a.amount ||
!!b.address - !!a.address ||
!!b.account - !!a.account ||
!!b.name - !!a.name
);
This works by coercing the undefined values to a boolean (i.e. undefined -> false) and then uses numerical comparison by again coercing the boolean values to type number (true -> 1, false -> 0), which results in sort comparator result values of [-1, 0, 1].
const data = [
{ account: true },
{ name: true },
{ amount: true },
{ address: true }
];
console.log("unsorted: ", JSON.stringify(data));
const sortedData = data.sort(
(a, b) =>
!!b.amount - !!a.amount ||
!!b.address - !!a.address ||
!!b.account - !!a.account ||
!!b.name - !!a.name
);
console.log("sorted: ", JSON.stringify(sortedData));

Related

Group and sort array

I have data that is coming from the server like this
let value = [
{
'commongId': 1,
'principal': true,
'creationDate': '2019-11-03:40:00'
},
{
'commongId': 2,
'principal': false,
'creationDate': '2017-10-25T01:35:00'
},
{
'commongId': 2,
'principal': true,
'creationDate': '2019-05-25T08:00:00'
},
{
'commongId': 1,
'principal': false,
'creationDate': '2018-11-25T09:40:00'
},
{
'commongId': 1,
'principal': false,
'creationDate': '2017-11-25T09:40:00'
},
{
'commongId': 2,
'principal': false,
'creationDate': '2018-05-25T08:00:00'
},
]
I want to transform it in a way that the courses are grouped by commonId, and that the principal course of each 'id' should appear first, and the rest of the courses belonging to the same commonId come after that principal course sorted by the creation date (asc).
So basically the output should be
let value = [
{
commongId: 1,
principal: true,
creationDate: '2019-11-03:40:00'
},
{
commongId: 1,
principal: false,
creationDate: '2017-11-25T09:40:00'
},
{
commongId: 1,
principal: false,
creationDate: '2018-11-25T09:40:00'
},
{
commongId: 2,
principal: true,
creationDate: '2019-05-25T08:00:00'
},
{
commongId: 2,
principal: false,
creationDate: '2017-10-25T01:35:00'
},
{
commongId: 2,
principal: false,
creationDate: '2018-05-25T08:00:00'
}
];
I have a working solution, which in my opinion looks horrible and too complicated.
// function to group the the courses by commonId
const groupBy = (data, keyFn) =>
data.reduce((agg, item) => {
const group = keyFn(item);
agg[group] = [...(agg[group] || []), item];
return agg;
}, {});
let transformedValue = groupBy(courses, item => item.commonId);
//removing the keys from the array of objects
transformedValue = Object.keys(transformedValue).map(k => transformedValue[k]);
// sorting each inner array by creationDate
transformedValue = transformedValue.map(el => {
let modified = el.sort((a, b) =>
moment(a.creationDate).diff(moment(b.creationDate))
);
// pushing the principal object of each array to the top
const foundIndex = modified.findIndex(element => element.principal);
if (foundIndex > -1) {
const foundElement = modified.find(element => element.principal);
modified.splice(foundIndex, 1);
modified.unshift(foundElement);
}
return modified;
});
// flattening the array to one level
transformedValue = transformedValue.flat();
// using the transformed value in the subscription
of(transformedValue).subscribe(p => {
this.dataToShow = p;
});
You could use sort like this.
const value=[{commongId:1,principal:true,creationDate:"2019-11-03:40:00"},{commongId:2,principal:false,creationDate:"2017-10-25T01:35:00"},{commongId:2,principal:true,creationDate:"2019-05-25T08:00:00"},{commongId:1,principal:false,creationDate:"2018-11-25T09:40:00"},{commongId:1,principal:false,creationDate:"2017-11-25T09:40:00"},{commongId:2,principal:false,creationDate:"2018-05-25T08:00:00"},];
value.sort((a, b) => a.commongId - b.commongId
|| b.principal - a.principal
|| a.creationDate.localeCompare(b.creationDate)
)
console.log(value)
The array will first be sorted based on commongId. If both have the same commongId, the subtraction will return 0. So, || will check the next expression because 0 is falsy value.
Then, it will be sorted based on principal. You can subtract 2 boolean values because it returns 1, -1 or 0 based on the value.
true - false === 1
false - true === -1
true - true === 0
If they still have the same value for commongId and principal, the array will be sorted based on the creationDate. Since the dates are in the ISO format, you can do a string comparison using localeCompare. If the date is in some other format, you could do
new Date(a.creationDate) - new Date(b.creationDate)
Use rxjs map and lodash groupBy.
of(response).pipe(
map(response => _groupBy(moveToFirst(response, item => item.principal === true)), 'commonId')
);
Where moveToFirst is a custom method to move the required item to Index 0.

How to create/merge object from splitted string array in TypeScript?

I have an array of objects like below;
const arr1 = [
{"name": "System.Level" },
{"name": "System.Status" },
{"name": "System.Status:*" },
{"name": "System.Status:Rejected" },
{"name": "System.Status:Updated" }
]
I am trying to split name property and create an object. At the end I would like to create an object like;
{
"System.Level": true,
"System.Status": {
"*": true,
"Rejected": true,
"Updated": true
}
}
What I have done so far;
transform(element){
const transformed = element.split(/:/).reduce((previousValue, currentValue) => {
previousValue[currentValue] = true;
}, {});
console.log(transofrmed);
}
const transofrmed = arr1.foreEach(element => this.transform(element));
The output is;
{System.Level: true}
{System.Status: true}
{System.Status: true, *: true}
{System.Status: true, Rejected: true}
{System.Status: true, Updated: true}
It is close what I want to do but I should merge and give a key. How can I give first value as key in reduce method? Is it possible to merge objects have same key?
You could reduce the splitted keys adn check if the last level is reached, then assign true, otherwise take an existent object or a new one.
const
array = [{ name: "System.Level" }, { name: "System.Status" }, { name: "System.Status:*" }, { name: "System.Status:Rejected" }, { name: "System.Status:Updated" }],
object = array.reduce((r, { name }) => {
var path = name.split(':');
last = path.pop();
path.reduce((o, k) => o[k] = typeof o[k] === 'object' ? o[k] : {}, r)[last] = true;
return r;
}, {});
console.log(object);
Use Array.reduce() on the list of properties. After splitting the path by :, check if there is second part. If there is a second part assign an object. Use object spread on the previous values, because undefined or true values would be ignored, while object properties would be added. If there isn't a second part, assign true as value:
const array = [{ name: "System.Level" }, { name: "System.Status" }, { name: "System.Status:*" }, { name: "System.Status:Rejected" }, { name: "System.Status:Updated" }];
const createObject = (arr) =>
arr.reduce((r, { name }) => {
const [first, second] = name.split(':');
r[first] = second ? { ...r[first], [second]: true } : true;
return r;
}, {});
console.log(createObject(array));

Using lodash orderBy or sortBy and handling null values

I'm having trouble using sortBy or orderBy to sort an array array by name where name may be null. I'd like to be able to sort by name in ascending order (A-Z) with the null values at the end.
Currently my code:
_sortBy(myArray, objectA => objectA.name)
Will return the null values at the beginning, then proceed to sort the objects with a name value after.
Thank you in advance for any help.
Lets assume your input is this:
var data=[
{name:'John Alexov'},
{name:null},
{name:'Alex Jones'},
{name:null}
]
if there are no numeric values in the names then a simple
_.sortBy(data, x => x.name)
or
_.orderBy(data, x => x.name) // since the default order is ["asc"]`
would give you the result you want with the nulls being after the names.
However lets assume you have numeric values in the names for some reason:
var data=[ {name:'John Alexov'}, {name:null}, {name:'Alex Jones'}, {name:null}, {name:null}, {name:'Mark 3rd'}, {name:'Bob Marley'}, {name:'john c'}, {name:'john 11th'}, {name:'john 1st'} ]
var result = data.sort((x,y) =>
x.name && y.name ? x.name.localeCompare(y.name, undefined, {numeric:
true}) : x.name ? -1 : y.name ? 1 : 0)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Then you would need something like localeCompare(y.name, undefined, {numeric: true}) to make sure john 1st is above john 11th.
localeCompare would also give you options when it comes to casing etc.
This solution worked for me and handles strings, number, booleans, and dates.
import { get, orderBy } from "lodash";
const items = [
{ id: 1, firstName: null, enabled: true, age: 30 },
{ id: 2, enabled: true, age: 31 },
{ id: 3, firstName: "", enabled: true, age: null },
{ id: 4, firstName: "A", enabled: false, age: undefined },
{ id: 5, firstName: "a", enabled: null, age: 10 },
{ id: 6, firstName: "b", enabled: undefined, age: 11 }
];
const sortIterate = (item, property) => {
// Null will be returned for any undefined values
const value = get(item, property, null);
// If value is null set to empty string to get proper sort order
// Works if comparing against strings, numbers, booleans, and dates.
return value === null ? "" : value;
};
// Single sort example
const orderedArray = orderBy(
items,
[(item) => sortIterate(item, "firstName")],
["asc"]
);
// Multi sort example
const multiOrderedArray = orderBy(
items,
[
(item) => sortIterate(item, "firstName"),
(item) => sortIterate(item, "age")
],
["asc", "desc"]
);
In vanilla js can do:
data.sort((a,b)=>{
if(a.name===b.name){
return 0
}else if(a.name===null || b.name===null){
return a.name ? -1 : 1;
}
return a.name.localeCompare(b.name);
})
console.log(data)
<script>
var data=[
{name:'F'},
{name:null},
{name:null},
{name:'A'}
]
</script>

Javascript can't group objects with 2 values

I have this object data:
[ RowDataPacket {
id: 59,
steamid: '76561198220437096',
product_id: 23,
status: 1,
date: 2017-12-18T17:27:19.000Z,
message: null,
name: 'CS.MONEY',
amount: 100,
website: 'csgo500' },
RowDataPacket {
id: 60,
steamid: '76561198220437096',
product_id: 24,
status: 1,
date: 2017-12-18T17:27:19.000Z,
message: null,
name: 'CS.MONEY',
amount: 250,
website: 'csgo500' },
RowDataPacket {
id: 61,
steamid: '76561198220437096',
product_id: 23,
status: 1,
date: 2017-12-18T17:27:19.000Z,
message: null,
name: 'CS.MONEY',
amount: 100,
website: 'csgo500' },
RowDataPacket {
id: 62,
steamid: '76561198345348530',
product_id: 6,
status: 1,
date: 2017-12-18T20:05:55.000Z,
message: null,
name: 'wal gruche',
amount: 100,
website: 'csgoatse' }
Im trying to sort this data with steamid and website, i managed to sort this only by one value like this:
var groupedOrders = {};
row.forEach(function(item){
var list = groupedOrders[item.steamid];
if(list){
list.push(item);
} else{
groupedOrders[item.steamid] = [item];
}
});
My idea was to make two dimensional array but for some reason i cant do it like this:
var list = groupedOrders[item.steamid][item.website];
It throws me an error "Cant read property ... of undefined"
Now my code looks like this:
var groupedOrders = {};
row.forEach(function(item){
var list = groupedOrders[item.steamid][item.website];
if(list){
list.push(item);
} else{
groupedOrders[item.steamid][item.website] = [item];
}
});
Do you have any ideas how to fix this errors?
The problem is that var list = groupedOrders[item.steamid][item.website] is actually saying:
var temp = groupedOrders[item.steamid];
var list = temp[item.website];
There is no entry at groupedOrders[item.steamid] and so line one sets temp to undefined. The second line tries to index into undefined which is an error.
You would have to split the code out and essentially do the whole one-key grouping twice:
var outerList = groupedOrders[item.steamid];
if (!outerList)
outerList = groupedOrders[item.steamid] = {};
var innerList = outerList[item.website];
if (innerList)
innerList.push(item);
else
outerList[item.website] = [item];
(I have not tested this code but it is the right shape.)
The following works by creating a recursive groupBy grouping function for each of the fields supplied as an argument.
These dynamically created groupBy functions are then invoked one by one, passing the result between, starting with the supplied data.
Each groupBy function instance creates an object and adds properties to it corresponding to the key values for the field being grouped.
By calling these groupBy functions successively, we create a progressively more nested tree of objects, with groups at each successive level marked as being groups using a symbol.
The final result is a nest (a tree!) of objects, with keys corresponding to the field used for indexing at that level.
Finally, we flatten the nest and the final order is visible.
const flatten = o => Object.values(o).reduce((acc, c) => (Array.isArray(c) ? [...acc, ...c] : typeof c === 'object' ? [...acc, ...flatten(c)] : [...acc, c]), []);
const flow = (...fns) => data => fns.reduce((acc, c) => c(acc), data);
const GROUP = Symbol('group');
const asGroup = (result = []) => ((result[GROUP] = true), result);
const isGroup = o => o[GROUP];
const groupBy = field => (data, key) =>
data.reduce((acc, c) =>
((key = c[field]), (acc[key] ?
(acc[key].push(c), acc) :
((acc[key] = asGroup([c])), acc))), {});
const recurse = (test) => (transform) => o =>
test(o)
? transform(o)
: Object.entries(o).reduce(
(acc, [k, v]) => (test(v) ?
((acc[k] = transform(v)), acc) :
((acc[k] = recurse(test)(transform)(v)), acc)), {});
const group = (...fields) => flow(...fields.map(flow(groupBy, recurse(isGroup))), flatten);
const rows = asGroup([
{
id: 0,
steamid: '2',
website: 'a'
},
{
id: 1,
steamid: '2',
website: 'b'
},
{
id: 2,
steamid: '2',
website: 'a'
},
{
id: 3,
steamid: '1',
website: 'b'
},
{
id: 4,
steamid: '0',
website: 'b'
}
]);
console.log(JSON.stringify(group('steamid', 'website')(rows), null, 2));

How can I get a unique array based on object property using underscore

I have an array of objects and I want to get a new array from it that is unique based only on a single property, is there a simple way to achieve this?
Eg.
[ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]
Would result in 2 objects with name = bill removed once.
Use the uniq function
var destArray = _.uniq(sourceArray, function(x){
return x.name;
});
or single-line version
var destArray = _.uniq(sourceArray, x => x.name);
From the docs:
Produces a duplicate-free version of the array, using === to test object equality. If you know in advance that the array is sorted, passing true for isSorted will run a much faster algorithm. If you want to compute unique items based on a transformation, pass an iterator function.
In the above example, the function uses the objects name in order to determine uniqueness.
If you prefer to do things yourself without Lodash, and without getting verbose, try this uniq filter with optional uniq by property:
const uniqFilterAccordingToProp = function (prop) {
if (prop)
return (ele, i, arr) => arr.map(ele => ele[prop]).indexOf(ele[prop]) === i
else
return (ele, i, arr) => arr.indexOf(ele) === i
}
Then, use it like this:
const obj = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]
obj.filter(uniqFilterAccordingToProp('abc'))
Or for plain arrays, just omit the parameter, while remembering to invoke:
[1,1,2].filter(uniqFilterAccordingToProp())
If you want to check all the properties then
lodash 4 comes with _.uniqWith(sourceArray, _.isEqual)
A better and quick approach
var table = [
{
a:1,
b:2
},
{
a:2,
b:3
},
{
a:1,
b:4
}
];
let result = [...new Set(table.map(item => item.a))];
document.write(JSON.stringify(result));
Found here
You can use the _.uniqBy function
var array = [ { id: 1, name: 'bob' }, { id: 2, name: 'bill' }, { id: 1, name: 'bill' },{ id: 2, name: 'bill' } ];
var filteredArray = _.uniqBy(array,function(x){ return x.id && x.name;});
console.log(filteredArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>
In the above example, filtering is based on the uniqueness of combination of properties id & name.
if you have multiple properties for an object.
then to find unique array of objects based on specific properties, you could follow this method of combining properties inside _.uniqBy() method.
I was looking for a solution which didn't require a library, and put this together, so I thought I'd add it here. It may not be ideal, or working in all situations, but it's doing what I require, so could potentially help someone else:
const uniqueBy = (items, reducer, dupeCheck = [], currentResults = []) => {
if (!items || items.length === 0) return currentResults;
const thisValue = reducer(items[0]);
const resultsToPass = dupeCheck.indexOf(thisValue) === -1 ?
[...currentResults, items[0]] : currentResults;
return uniqueBy(
items.slice(1),
reducer,
[...dupeCheck, thisValue],
resultsToPass,
);
}
const testData = [
{text: 'hello', image: 'yes'},
{text: 'he'},
{text: 'hello'},
{text: 'hell'},
{text: 'hello'},
{text: 'hellop'},
];
const results = uniqueBy(
testData,
item => {
return item.text
},
)
console.dir(results)
In case you need pure JavaScript solution:
var uniqueProperties = {};
var notUniqueArray = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ];
for(var object in notUniqueArray){
uniqueProperties[notUniqueArray[object]['name']] = notUniqueArray[object]['id'];
}
var uniqiueArray = [];
for(var uniqueName in uniqueProperties){
uniqiueArray.push(
{id:uniqueProperties[uniqueName],name:uniqueName});
}
//uniqiueArray
unique array by id property with ES6:
arr.filter((a, i) => arr.findIndex(b => b.id === a.id) === i); // unique by id
replace b.id === a.id with the relevant comparison for your case

Categories

Resources