Home > Net >  Is there a good way to sort an object within an array by multiple parameters?
Is there a good way to sort an object within an array by multiple parameters?

Time:11-08

I really need some help with a homework problem I have been given. I don't want answers to the question, but I would like some help with knowing what methods to use.

My problem is:

The product owner on your development team believes they've seen a pattern as to which customers purchase the most socks. To verify, you've been asked to write a function that processes an array of customer objects and return a new array that contains only customers that match ANY of the following criteria:

  1. name starts with a 'C' (upper or lowercase)
  2. address contains no undefined fields
  3. the city is Peoria and the state is AZ
  4. membership level is GOLD or PLATINUM unless the customer is younger than 29, then SILVER is okay too

The array of customer objects will have the following schema:

const customers = [
    {
        name: 'Sam',
        address: {
            street: '1234 W Bell Rd',
            city: 'Phoenix',
            zip: '85308',
            state: 'AZ'
        },
        membershipLevel: 'GOLD',
        age: 32
    },
    //more customers with the same schema
];

Note: The solution to this problem does not require the use of callbacks. You will also need to use dot notation to access the properties. For example, customers[0].name would return "Sam".

As you can see, I have to sort this array by a few different parameters involving strings and part of strings. However, I have tried and failed to use the typical Array.sort() method as I'm actually sorting the objects, and I can't find a method to sort objects by parts of a string. Additionally, I will need help keeping it all within a separate array.

The thing that I think would help me the most would be to give me the method I should use for each of the criteria in the problem above. If so, I can look up and hopefully learn the methods myself. I am willing to accept any and all advice. Thank you for your time in reading this and helping me.

CodePudding user response:

You can achieve this requirement with the help of these methods :

Live Demo :

const customers = [
  {
    name: 'Sam',
    address: {
      street: '1234 W Bell Rd',
      city: 'Phoenix',
      zip: '85308',
      state: 'AZ'
    },
    membershipLevel: 'GOLD',
    age: 32
  },
  {
    name: 'Carlo',
    address: {
      street: '1250 Ring Rd',
      city: 'Peoria',
      zip: '85308',
      state: 'AZ'
    },
    membershipLevel: 'SILVER',
    age: 28
  }
];

const res = customers.filter(customer => {
  const nameCondition = customer.name.startsWith('C');
  const addressCondition = !Object.values(customer.address).some(val => !val);
  const cityStateCondition = customer.address.city === 'Peoria' && customer.address.state === 'AZ';
  const membershipCondition = (customer.age > 29) ? ['GOLD', 'PLATINUM'].includes(customer.membershipLevel) : ['GOLD', 'PLATINUM', 'SILVER'].includes(customer.membershipLevel)
  return nameCondition && addressCondition && cityStateCondition && membershipCondition;
});

console.log(res);

CodePudding user response:

You can use array#filter to return an array of the elements fitting your requirements

The filter() method creates a shallow copy of a portion of a given array, filtered down to just the elements from the given array that pass the test implemented by the provided function.

The first parameter is a function to test each element of the array. Return a value that coerces to true to keep the element, or to false otherwise.

Example 1

//REM: name starts with a 'C' (upper or lowercase)
console.log(
  [
      {
          name: 'Sam',
          address: {
              street: '1234 W Bell Rd',
              city: 'Phoenix',
              zip: '85308',
              state: 'AZ'
          },
          membershipLevel: 'GOLD',
          age: 32
      },
      {
          name: 'Caludia',
          address: {
              street: '1234 W Bell Rd',
              city: 'Phoenix',
              zip: '85308',
              state: 'AZ'
          },
          membershipLevel: 'GOLD',
          age: 32
      },
  ].filter(item => {
    return item.name.startsWith('c') || item.name.startsWith('C')
  })
)

Example 2

//REM: name starts with a 'C' (upper or lowercase)
//REM: the city is Peoria and the state is AZ
console.log(
  [
      {
          name: 'Sam',
          address: {
              street: '1234 W Bell Rd',
              city: 'Phoenix',
              zip: '85308',
              state: 'AZ'
          },
          membershipLevel: 'GOLD',
          age: 32
      },
      {
          name: 'Caludia',
          address: {
              street: '1234 W Bell Rd',
              city: 'Peoria',
              zip: '85308',
              state: 'AZ'
          },
          membershipLevel: 'GOLD',
          age: 32
      },
  ].filter(item => {
    return (
      (item.name.startsWith('c') || item.name.startsWith('C')) &&
      (item.address?.city === 'Peoria') &&
      (item.address?.state === 'AZ')
    )
  })
)

Just add each of your four requirements step by step until you reach the goal. Half of requirement 2 is already included in requirement 3 if the structure is consistent.

CodePudding user response:

You can use filter method to create a new array of objects that satisfies the conditions that you listed. Although you can use filter by writing your own function directly inside of it, you also have another option where you write the same function and pass it to filter method as argument which might be better sometimes in terms of simplicity and maintainability. Below is the function that checks if the object satisfies the conditions.

First Approach

const customers = [
  {
    name: "Sam",
    address: {
      street: "1234 W Bell Rd",
      city: "Phoenix",
      zip: "85308",
      state: "AZ",
    },
    membershipLevel: "GOLD",
    age: 32,
  },
  {
    name: "Cindy",
    address: {
      street: "1286834 E Bull Rd",
      city: "Peoria",
      zip: "85308",
      state: "AZ",
    },
    membershipLevel: "GOLD",
    age: 32,
  },
  {
    name: "Chloe",
    address: {
      street: "1234 S Dull Rd",
      city: "Peoria",
      zip: "85308",
      state: "AZ",
    },
    membershipLevel: "SILVER", 
    age: 25, 
  },
  {
    name: "Charlie",
    address: {
      street: "1234 N Dell Rd", 
      city: "Peoria",
      zip: "85308",
      state: "VA",
    },
    membershipLevel: "SILVER", 
    age: 20,
  },
];

// check if a customer satisfies all conditions
function checkCustomer(customerObject) { 

  // object destructuring
  const {
    name,
    address: { city, state },
    membershipLevel,
    age,
  } = customerObject;

  if (
    (name.startsWith("c") || name.startsWith("C")) &&
    city === "Peoria" &&
    state === "AZ" &&
    ((age >= 29 &&
      (membershipLevel === "GOLD" || membershipLevel === "PLATINUM")) ||
      (age < 29 && membershipLevel === "SILVER"))
  ) {
    return true;
  }

  return false;
}

// write names of customers that satisfy conditions 
customers.filter(checkCustomer).forEach(customer => {
    console.log(customer.name); 
})

However, it is often a good idea not to write a block of if statement that is hard to read and work with. Thus you can write the same function by dividing conditions into smaller parts and check them seperately so you can eliminate things easily. Below is the same function but it returns true if all the tests (so to say) are passed.

Second Approach

const customers = [
  {
    name: "Sam",
    address: {
      street: "1234 W Bell Rd",
      city: "Phoenix",
      zip: "85308",
      state: "AZ",
    },
    membershipLevel: "GOLD",
    age: 32,
  },
  {
    name: "Cindy",
    address: {
      street: "1286834 E Bull Rd",
      city: "Peoria",
      zip: "85308",
      state: "AZ",
    },
    membershipLevel: "GOLD",
    age: 36,
  },
  {
    name: "Chloe",
    address: {
      street: "1234 S Dull Rd",
      city: "Peoria",
      zip: "85308",
      state: "AZ",
    },
    membershipLevel: "SILVER", 
    age: 25,
  },
  {
    name: "Charlie",
    address: {
      street: "1234 N Dell Rd", 
      city: "Peoria",
      zip: "85308",
      state: "VA", 
    },
    membershipLevel: "SILVER", 
    age: 20,
  },
];

// check if a customer satisfies all conditions
function checkCustomer(customerObject) { 

  // object destructuring
  const {
    name,
    address: { city, state },
    membershipLevel,
    age,
  } = customerObject;

  // name should begin with c (uppercase or lowercase)
  if (!(name.startsWith("c") || name.startsWith("C"))) {
    return false;
  }

  // address info can not contain empty field
  for (const addressKey in customerObject.address) {
    if (customerObject.address[addressKey] === "") {
      return false;
    }
  }

  // city and state should match
  if (!(city === "Peoria" && state === "AZ")) {
    return false;
  }

  // membership level can be gold or platinum if age >= 29
  // otherwise silver should suffice 
  if (
    !(
      (age >= 29 &&
        (membershipLevel === "GOLD" || membershipLevel === "PLATINUM")) ||
      (age < 29 && membershipLevel === "SILVER")
    )
  ) {
    return false;
  }

  return true;
}

// write names of customers that satisfy conditions
customers.filter(checkCustomer).forEach(customer => {
    console.log(customer.name); 
})

By the way, the second condition that you have listed, address contains no undefined fields, is not clear. In javascript, undefined has a special meaning and it is one of the primitive types that you see when you try to use a variable without assigning a value. In this case, you will see undefined if you try to reach an object using a key that does not exist (ie. customer[0].address.houseNumber). Therefore, I added a part in the function that checks whether any of the address info is empty or not. If you want to check if a variable or return type of a function is undefined, you can use strict equality.

Lastly, you can check sort array of objects by a key to see how you can sort your array.

  • Related