2

I have an array like so:

var myarray = [
    {"scaleId":"001","currentWeight":0.200}
    {"scaleId":"002","currentWeight":0.300},
    {"scaleId":"001","currentWeight":0.255},
    {"scaleId":"002","currentWeight":0.000},
    {"scaleId":"003","currentWeight":0.340},
    ]

and I want to retain the objects which have unique scaleId. So, for the above example, the output would like ( any random object can be retained if the scaleId is duplicate):

myarray = [
    {"scaleId":"001","currentWeight":0.200}
    {"scaleId":"002","currentWeight":0.300},
    {"scaleId":"003","currentWeight":0.340},
    ]

I'm already using underscore library in my application, so I'm fine with the implementation using underscore. I had come up with a solution but it is not working as expected. So, any help will be great.

4 Answers 4

1

Use reduce and inside callback check if in accumulator object there is a key with same scaleId. If so then add the value to that key. Finally use Object.values to create an array of values

var myarray = [{
    "scaleId": "001",
    "currentWeight": 0.200
  },
  {
    "scaleId": "002",
    "currentWeight": 0.300
  },
  {
    "scaleId": "001",
    "currentWeight": 0.255
  },
  {
    "scaleId": "002",
    "currentWeight": 0.000
  },
  {
    "scaleId": "003",
    "currentWeight": 0.340
  }
]

let data = myarray.reduce((acc, curr) => {
  if (!acc[curr.scaleId]) {
    acc[curr.scaleId] = curr
  }
  return acc;
}, {});

console.log(Object.values(data))

Sign up to request clarification or add additional context in comments.

Comments

1

Maps and Sets are often the appropriate structures to maintain uniqueness. Here is a simple function that uses a Map to maintain a single unique value given a property name:

const uniqByProp = (prop) => (xs) =>
  [... new Map (xs .map ((x) => [x [prop], x])) .values ()]

var myarray = [{scaleId: "001", currentWeight: 0.200}, {scaleId: "002", currentWeight: 0.300}, {scaleId: "001", currentWeight: 0.255}, {scaleId: "002", currentWeight: 0.000}, {scaleId: "003", currentWeight: 0.340}]

console .log (
  uniqByProp ('scaleId') (myarray)
)
.as-console-wrapper {min-height: 100% !important; top: 0}

This version keeps the last matching value. If you want the first matching one, it would be only slightly more complex.

4 Comments

Out of interest, is there a resource I can read about the particular code style you use? I don't see this used often, though I'm aware it's common among those that favor FP.
@PatrickRoberts: I'm not sure what about my style interests you. I am an FP fan, so I guess I'm matching some stereotype! I prefer curried functions. I prefer simple single-purpose but ideally generic functions, but that just seems common sense to me. I'm willing to write fairly wide lines, but I like some unusual whitespace (e.g. xs .map (...) rather than xs.map(...), but that's a minor idiosyncracy more than a consistent style. Or is it something else?
It was the whitespace usage I was noting particularly. There's no standard for that then? I've seen that used quite consistently when FP-oriented people write JavaScript.
@PatrickRoberts. I know of no styleguide that suggests it. I borrowed it and some other formatting ideas from another SO user who has since changed whitespace style. Posts from that user contain a wealth of useful techniques. The formatting is clearly derived from LISP-like language formatting.
0

You can do it this way (if you are using ES6/ES2015 or later) Using Set to filter unique scaleIds first and feeding a new array with the Array.find method

var myarray = [
    {"scaleId":"001","currentWeight":0.200},
    {"scaleId":"002","currentWeight":0.300},
    {"scaleId":"001","currentWeight":0.255},
    {"scaleId":"002","currentWeight":0.000},
    {"scaleId":"003","currentWeight":0.340},
]

let scaleIds = [...new Set(myarray.map(item => item.scaleId))];
let filtered = []
scaleIds.forEach(scaleId => filtered.push(myarray.find(item => item.scaleId === scaleId)))

console.log(filtered)

Comments

0

You can also use Array.prototype.reduceRight() to simplify aggregation logic:

let myarray = [
  { scaleId: '001', currentWeight: 0.2 },
  { scaleId: '002', currentWeight: 0.3 },
  { scaleId: '001', currentWeight: 0.255 },
  { scaleId: '002', currentWeight: 0 },
  { scaleId: '003', currentWeight: 0.34 }
];

myarray = Object.values(
  myarray.reduceRight(
    (acc, cur) => (acc[cur.scaleId] = cur, acc),
    {}
  )
).reverse(); // optional if order of output matters to you

console.log(myarray);

or construct a Map from key-value pairs generated using Array.prototype.map():

let myarray = [
  { scaleId: '001', currentWeight: 0.2 },
  { scaleId: '002', currentWeight: 0.3 },
  { scaleId: '001', currentWeight: 0.255 },
  { scaleId: '002', currentWeight: 0 },
  { scaleId: '003', currentWeight: 0.34 }
];

myarray = Array.from(
  new Map(
    myarray.map(
      val => [val.scaleId, val]
    ).reverse()
  ).values()
).reverse(); // optional if order of output matters to you

console.log(myarray);

We use Array.prototype.reverse() on the array passed to the constructor to ensure that the insertion order allows earlier values to take precedence over later values.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.