I have an array with data similar to this but with more fields to sort from:
const shirts= [
{
type: "T-shirt",
size: "L",
color: "Black",
},
{
type: "Dress Shirt",
size: "S",
color: "Brown",
},
{
type: "Sweatshirt",
size: "M",
color: "Red",
},
{
type: "Polo",
size: "XS",
color: "Pink",
},
...]
I have a sort function that works differently based on what is selected, for example if the user sorts by size it needs to go from XS to XL but any other option it needs to sort alphabetically. This is what I have:
//sort is the option the user decided to sort by
SortedShirts(sort,shirts){
var newShirtArray=[];
var size1=0;
var size2=0;
if(sort === "type"){
newShirtArray= shirts.sort(function(shirt1,shirt2){
if (shirt1.type> shirt2.type){
return 1;
}
else{
return -1;
}
});
}
else if(sort === "color"){
newShirtArray = shirts.sort(function(shirt1,shirt2){
if (shirt1.color > shirt2.color){
return 1;
}
else{
return -1;
}
});
}
else if(sort === "size"){
newShirtArray = shirts.sort(function(shirt1,shirt2){
if(shirt1.size==="XS"){
size1=0;
}
else if(shirt1.size==="S"){
size1=1;
}
else if(shirt1.size==="M"){
size1=2;
}
else if(shirt1.size==="L"){
size1=3;
}
else if(shirt1.size==="XL"){
size1=4;
}
if(shirt2.size==="XS"){
size2=0;
}
else if(shirt2.size==="S"){
size2=1;
}
else if(shirt2.size==="M"){
size2=2;
}
else if(shirt2.size==="L"){
size2=3;
}
else if(shirt2.size==="XL"){
size2=4;
}
if (size1 > size2){
return 1;
}
else{
return -1;
}
});
}
This seems repetitive to me because type and color sort the same way just with a different field and I feel I can put that into one sort but I am unsure on how to do it. I'm wondering if there is something like this
if(sort === "type" || sort === "color"){
newShirtArray= shirts.sort(function(shirt1,shirt2){
if (shirt1.fieldOf(sort) > shirt2.fieldOf(sort)){
return 1;
}
else{
return -1;
}
});
}
Or another way to reduce my code?
You can use a switch statement:
switch (sort) {
case 'color':
case 'type':
newShirtArray = shirts.sort(/* ... */);
break;
case 'size':
newShirtArray = shirts.sort(/* ... */);
break;
}
For converting you shirt size to number, you can use an object:
const ShirtSizes = {
XS: 0, S: 1, M: 2, L: 3, XL: 4,
};
const shirt1Size = ShirtSizes[shirt1.size];
If your environment allows this, use more concise ES2015 arrow functions:
case 'size':
newShirtArray = shirts.sort((s1, s2) => ShirtSizes[s1.size] - ShirtSizes[s2.size]);
break;
For type and color, as you guessed, you can use this:
case 'color':
case 'type':
newShirtArray = shirts.sort((s1, s2) => {
if (s1[sort] > s2[sort]) {
return 1;
} else if (s1[sort] < s2[sort]) {
return -1;
} else {
return 0;
}
});
I hope this will help a bit.
You can access JSON object's key using bracket.
const shirts = [
{
type: "T-shirt",
size: "L",
color: "Black",
},
{
type: "Dress Shirt",
size: "S",
color: "Brown",
},
{
type: "Sweatshirt",
size: "M",
color: "Red",
},
{
type: "Polo",
size: "XS",
color: "Pink",
},
]
function SortedShirts(sort, shirts) {
var newShirtArray = [];
var size1 = 0;
var size2 = 0;
if (sort === "type" || sort === "color") {
newShirtArray = shirts.sort(function (shirt1, shirt2) {
if (shirt1[sort] > shirt2[sort]) {
return 1;
}
else {
return -1;
}
});
}
else if (sort === "size") {
const sizes = ['XS', 'S', 'M', 'L', 'XL']
newShirtArray = shirts.sort(function (shirt1, shirt2) {
const size1 = sizes.indexOf(shirt1.size)
const size2 = sizes.indexOf(shirt2.size)
if (size1 > size2) {
return 1;
}
else {
return -1;
}
});
}
}
Related
I wrote a module that has sorting methods. I also tried to make it more flexible and destructure parameters to sort anything i will set creating object (more or less) but I don't know how to do it inside the sort method. What i tried is in the equal2 last method, but obviesly is not working fine. Does anyone have clue what should i correct in this example?
let products = [ { name: "Grejpfrut", calories: 170, color: "czerwony", sold: 8200 },
{ name: "Pomarańcza", calories: 160, color: "pomarańczowy", sold: 12101 },
{ name: "Cola", calories: 210, color: "karmelowy", sold: 25412 },
{ name: "Cola dietetyczna", calories: 0, color: "karmelowy", sold: 43922 },
{ name: "Cytryna", calories: 200, color: "bezbarwny", sold: 14983 },
{ name: "Malina", calories: 180, color: "różowy", sold: 8200 },
{ name: "Piwo korzenne", calories: 200, color: "karmelowy", sold: 9909 },
{ name: "Woda", calories: 0, color: "bezbarwny", sold: 62123 }
];
class compareSold {
constructor(param1, param2) {
this.param1 = param1 ;
this.param2 = param2 ;
// this.products = products ;
const { calories, sold } = products;
this.sortType = {
asc: (param1, param2) => {
// console.log(param1);
if (param1.sold > param2.sold) {
return 1
} else if (param1.sold === param2.sold) {
return 0
} else {
return -1
}
},
dsc: (param1, param2) => {
// console.log(param1);
if (param2.sold > param1.sold) {
return 1
} else if (param1.sold === param2.sold) {
return 0
} else {
return -1
}
},
eqal: (param1, param2) => {
// console.log(param1);
if (param1.calories > param2.calories) {
return 1
} else if (param1.calories === param2.calories) {
console.log('a wowowiwa !!!');
} else {
return -1
}
},
eqal2: (param1, param2) => {
console.log(param1);
if (param1.sold > param2.sold) {
return 1
} else if ({sold} === {sold}) {
console.log('a wowowiwa !!!');
} else {
return -1
}
}
}
}
}
const { calories, sold } = products; const compareSoldASCPass = new
compareSold();
// function calories ({calories}) { console.log(calories)}; //
// Type of sorting(asc, dsc) ////////////////////////////////////////
const compareSoldASC =
products.sort(compareSoldASCPass.sortType.eqal2)
// console.log(compareSoldASC);
module.exports = compareSoldASC;
I have an object name config in which i have "from" and "to".
const config = {
"0": {
id: 0,
from: 0,
to: 10,
hex: null
},
"1": {
id: 1,
from: 11,
to: 20,
hex: null
},
"2": {
id: 2,
from: 21,
to: 30,
hex: null
},
"3": {
id: 3,
from: 31,
to: 40,
hex: null
},
"4": {
id: 4,
from: 41,
to: 50,
hex: null
}
};
I have to check that now range will contradict with each other eg: form:0 => to:10 and from:5=> to:20
here the from of second is contradict because 5 lies between 0 to 10
i have tried following but doesn't full-fill my requirement
function found(conf) {
let isFound = false;
for (let obj in conf) {
for (let x in conf) {
if (
conf[obj].id !== conf[x].id &&
(conf[x].from >= conf[obj].from && conf[x].to <= conf[obj].from)
) {
console.log(conf[obj], conf[x]);
isFound = true;
break;
}
}
if (isFound) break;
}
return isFound;
}
console.log(found(config));
Create a single array by combining all ranges
const arr = Object.entries(config).map(([a, b]) => b).flatMap(({from, to}) => RANGE(from, to))
where RANGE is method which return array of given ranges:
const RANGE = (a,b) => Array.from((function*(x,y){
while (x <= y) yield x++;
})(a,b));
Then find duplicates in the given arr using the following function:
function findDuplicate(array) {
var object = {};
var result = [];
array.forEach(function(item) {
if (!object[item]) object[item] = 0;
object[item] += 1;
});
for (var prop in object) {
if (object[prop] >= 2) {
result.push(prop);
}
}
return result;
}
const duplicates = findDuplicate(arr)
Then finally check duplicates.length
Try renaming your variables so they make sense.
Your logic is: IDs don't match and inner is after outer, but before outer's from.
There will never be a case where this will return true.
const config = {
"0": { id: 0, from: 0, to: 10, hex: null },
"1": { id: 1, from: 11, to: 20, hex: null },
"2": { id: 2, from: 21, to: 30, hex: null },
"3": { id: 3, from: 31, to: 40, hex: null },
"4": { id: 4, from: 41, to: 50, hex: null }
};
console.log(found(config));
function found(conf) {
for (let outer in conf) {
for (let inner in conf) {
let idsDontMatch = conf[outer].id !== conf[inner].id;
let innerFromGteOuterFrom = conf[inner].from >= conf[outer].from;
let innerToLteOuterFrom = conf[inner].to <= conf[outer].from;
let innerAfterOuterButBeforeOuterFrom = innerFromGteOuterFrom && innerToLteOuterFrom;
if (idsDontMatch && innerAfterOuterButBeforeOuterFrom) {
console.log(conf[outer], conf[inner]);
return true;
}
}
}
return false;
}
.as-console-wrapper { top: 0; max-height: 100% !important; }
var persons = [
{ Color: "Gold", Location: ["Down"] },
{ Color: "Silver", Location: ["Up", "Down"] },
{ Color: "Silver", Location: ["Up"] }
];
var criteria = [
{ Field: "Color", Values: ["Silver"] },
{ Field: "Location", Values: ["Up", "Down"] }
];
Here field color is of type String, and Location is an array.
I have persons, and then there is a filter criteria. I need an output so that all the values selected in the filter needs to match with the data. So in the data provided, only those records should be visible if Silver, Up and Down are available in a record. (Note the AND parameter, there is no OR condition anywhere).
So the output will be:
{ Color: "Silver", Location: ["Up", "Down"] }
Now if the filter criteria is:
var criteria = [
{ Field: "Color", Values: ["Silver"] },
{ Field: "Location", Values: ["Up"] }
];
the output will be:
{ Color: "Silver", Location: ["Up", "Down"] },
{ Color: "Silver", Location: ["Up"] }
So you see all the values in the filter should match with the records.
I broke down the problem into separate functions. It's way more verbose than your solution but I do think it's more readable.
Also: it does work.
var persons = [
{ Color: "Gold", Location: ["Down"] },
{ Color: "Silver", Location: ["Up", "Down"] },
{ Color: "Silver", Location: ["Up"] }
];
var criteria = [
{ Field: "Color", Values: ["Silver"] },
{ Field: "Location", Values: ["Up", "Down"] }
];
const arraysEqual = (arr1, arr2) => {
// Very simple array comparison.
if (arr1.length !== arr2.length) return false;
arr1 = arr1.sort();
arr2 = arr2.sort();
for(let i=0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) return false;
}
return true;
};
let result = persons.filter(person => {
// All criteria must match.
for (let criterium of criteria) {
if (criterium.Field === 'Color') {
if (person.Color !== criterium.Values[0]) return false;
}
if (criterium.Field === 'Location') {
if (!arraysEqual(person.Location, criterium.Values)) return false;
}
}
// We didn't *not* match for anything, so we matched!
return true;
});
console.log(result);
/*
{ Color: "Silver", Location: ["Down", "Up"] }
*/
See https://repl.it/#niels_bom/GoldenIdealMicrobsd for a running example.
After third edit, now I am clear on what you want - to have an array as a subset of the other.
var result = persons.filter(function (person) {
return criteria.every(function (c) {
var value = person[c.Field];
if (typeof value === 'object') {
return c.Values.length<=value.length &&
c.Values.every(function(v,i){return value.includes(v) ;});
}
else
return c.Values.indexOf(value) > -1;
})
})
I also updated your jsfiddle : https://jsfiddle.net/gzL42dna/1/
Also checkout: How to compare arrays in JavaScript?
I believe this works. Using a Set helps a lot with partial matches. Tell me if you would like an explanation of the code.
var persons = [
{ Color: "Gold", Location: ["Down"] },
{ Color: "Silver", Location: ["Up", "Down"] },
{ Color: "Silver", Location: ["Up"] }
];
var criteria = [
{ Field: "Color", Values: ["Silver"] },
{ Field: "Location", Values: ["Up", "Down"] }
];
console.log(match(persons, criteria));
function match(persons, criteria) {
let personMatches = [...persons]
for (let i=0; i < criteria.length; i++) {
let {Field, Values} = criteria[i]
personMatches = personMatches.filter(obj => {
if (Array.isArray(obj[Field])) {
return hasMatches(obj[Field], Values)
} else {
return Values.includes(obj[Field])
}
})
}
return personMatches
}
function hasMatches(arr1, criteria) {
let criteriaSet = new Set(criteria)
let personsSet = new Set(arr1)
for (let el of criteriaSet) {
if (!personsSet.has(el)) return false
}
return true
}
I want to create an optimized way to classify a value in certain labels.
Example
Input: 12.2, 61, 77.7
Output: "bad", "poor", "good"
I create a simple if, but maybe exist a better way
let output = null;
if (rating <= 60){ output = 'bad'}
if (rating > 60){ output = 'poor'}
if (rating > 70){ output = 'good'}
if (rating > 90){ output = 'excellent'}
You could use Array#some and iterate through an array of objects for the rating. The advantage is a good maintainable object.
ratings = [
{ value: 60, label: 'bad' },
{ value: 70, label: 'poor' },
{ value: 90, label: 'good' },
{ value: Infinity, label: 'excellent' }
]
function rating(v) {
var ratings = [{ value: 60, label: 'bad' }, { value: 70, label: 'poor' }, { value: 90, label: 'good' }, { value: Infinity, label: 'excellent' }],
label;
ratings.some(function (a) {
if (v <= a.value) {
label = a.label;
return true;
}
});
return label;
}
console.log([12.2, 61, 77.7].map(rating));
.as-console-wrapper { max-height: 100% !important; top: 0; }
ES6 with Array#find
var ratings = [{ value: 60, label: 'bad' }, { value: 70, label: 'poor' }, { value: 90, label: 'good' }, { value: Infinity, label: 'excellent' }],
rating = v => ratings.find(a => v <= a.value).label;
console.log([12.2, 61, 77.7].map(rating));
.as-console-wrapper { max-height: 100% !important; top: 0; }
One better way is to creatively use switch:
var output = null;
var rating = parseInt(prompt("Rating?"));
switch (true) {
case (rating <= 60):
output = 'bad';
break;
case (rating > 90):
output = 'excellent';
break;
case (rating > 70):
output = 'good';
break;
case (rating > 60):
output = 'poor';
break;
}
console.log(output);
Here, the correct organisation of the lines are very important.
One fast way of doing this if you are fine your memory.
var limits = [60,70,90,100],
rates = ["bad","poor","good","excellent"],
grades = limits.reduce((g,c,i,a) => i ? g.concat(Array(c-a[i-1]).fill(rates[i]))
: g.concat(Array(c).fill(rates[0])),[]),
notes = [12.2, 61, 77.7, 89.5];
notes.forEach(n => console.log(grades[Math.round(n)]));
I have a large-ish dataset (from 400 - 4,000 objects stored in an array), and I'm trying to filter them by a user-selected field.
Right now I'm using this function, found on another SO question:
var sort = function (prop, arr) {
prop = prop.split('.');
var len = prop.length;
arr.sort(function (a, b) {
var i = 0;
while( i < len ) {
a = a[prop[i]];
b = b[prop[i]];
i++;
}
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
});
return arr;
};
Sample data - I want to sort the objects by the friends count:
var data = [
{
name: 'Jim',
friends: {
count: 20,
url: 'http://foo.com'
}
},{
name: 'Lucy',
},{
name: 'Phil',
friends: {
count: 450,
url: 'http://moo.com'
}
}
];
Notice how "Lucy" doesn't have a friends object - so when I run sort('friends.count', data);, the script breaks.
Ideally I'd like the objects which don't have the property that I'm sorting by to be put at the end of the array. Any ideas on how this can be achieved?
For example,
var data = [
{
name: 'Jim',
friends: {
count: 20,
url: 'http://foo.com'
}
},{
name: 'Lucy',
},{
name: 'Phil',
friends: {
count: 450,
url: 'http://moo.com'
}
}
];
safeGet = function(obj, prop, defaultValue) {
try {
return obj[prop]
} catch(e) {
return defaultValue
}
}
data.sort(function(x, y) {
return (
safeGet(x.friends, 'count', Infinity) -
safeGet(y.friends, 'count', Infinity));
});
document.write("<pre>" + JSON.stringify(data,0,3));
If the whole property chain (friends.count) is dynamic, change safeGet so that it iterates the list of props:
var data = [
{
name: 'Jim',
friends: {
count: 20,
url: 'http://foo.com'
}
},{
name: 'Lucy',
},{
name: 'Phil',
friends: {
count: 450,
url: 'http://moo.com'
}
}
];
safeGet = function(obj, props, defaultValue) {
try {
return props.split('.').reduce(function(obj, p) {
return obj[p];
}, obj);
} catch(e) {
return defaultValue
}
}
data.sort(function(x, y) {
return (
safeGet(x, 'friends.count', Infinity) -
safeGet(y, 'friends.count', Infinity));
});
document.write("<pre>" + JSON.stringify(data,0,3));
If you want people with no friends to go first, not last, change Infinity to -Infinity.
Your function can be modified to check for the existence of a property:
var sort = function (prop, arr) {
prop = prop.split('.');
var len = prop.length;
arr.sort(function (a, b) {
var i = 0;
var key;
while( i < len ) {
key = prop[i];
if(!a.hasOwnProperty(key)) return 1;
if(!b.hasOwnProperty(key)) return -1;
a = a[key];
b = b[key];
i++;
}
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
});
return arr;
};
This way it will be working. I made a jsbin for the example.
#georg's answer wouldn't work with property selected dynamically.