0

I have an array of objects and I need to get the "code" value where "selected" is true. At any instance, selected will be true only for one item. I have an implementation done and is there any way to improve upon it?

const data = [
  {
    "id": "1",
    "code": "A",
    "selected": true,
    "defaultCollapsed": false,
    "label": "A",
    "items": [
      {
        "id": "A1",
        "code": "A1",
        "label": "A-1 PP",
        "selected": true,
        "defaultCollapsed": false,
        "url": "#A1"
      },
      {
        "id": "A2",
        "code": "A2",
        "label": "A-2 ST",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#A2"
      },
      {
        "id": "A3",
        "code": "A3",
        "label": "A-3 SR",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#A3"
      },
      {
        "id": "A4",
        "code": "A4",
        "label": "A-4 BLS",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#A4"
      },
      {
        "id": "A5",
        "code": "A5",
        "label": "A-5 BIFO",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#A5"
      },
      {
        "id": "A6",
        "code": "A6",
        "label": "A-6 VA",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#A6"
      }
    ]
  },
  {
    "id": "2",
    "code": "B",
    "selected": false,
    "defaultCollapsed": true,
    "label": "B. ECQG",
    "items": [
      {
        "id": "B1",
        "code": "B1",
        "label": "B-1 VR",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#B1"
      }
    ]
  },
  {
    "id": "3",
    "code": "C",
    "selected": false,
    "defaultCollapsed": true,
    "label": "C. CRR",
    "items": [
      {
        "id": "C1",
        "code": "C1",
        "label": "C-1 RSR",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#C1"
      },
      {
        "id": "C2",
        "code": "C2",
        "label": "C-2 Other",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#C2"
      }
    ]
  }
]

console.log(_.find(_.find(data, item => _.find(item.items, item1 => item1.selected)).items, item3 => item3.selected).code)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

Expected End output is “A1” for the above example. Please advice.

4
  • please add the wanted result. Commented Dec 19, 2019 at 19:23
  • Added Nina..... Commented Dec 19, 2019 at 19:24
  • Do you require a lodash solution? A native JavaScript .find() would likely be a lot more readable than the nested lodash _.find() approach. (Albeit, there may be an even better alternative lodash approach.) Commented Dec 19, 2019 at 19:30
  • @TylerRoper Thats fine. I used lodash and we have eslint-plugin-lodash setup in our code Commented Dec 19, 2019 at 19:31

3 Answers 3

3

Flatten the items to a single array via Array.flatMap(), and then use Array.find() to get the selected item:

const data = [{"id":"1","code":"A","selected":true,"defaultCollapsed":false,"label":"A","items":[{"id":"A1","code":"A1","label":"A-1 PP","selected":true,"defaultCollapsed":false,"url":"#A1"},{"id":"A2","code":"A2","label":"A-2 ST","selected":false,"defaultCollapsed":true,"url":"#A2"},{"id":"A3","code":"A3","label":"A-3 SR","selected":false,"defaultCollapsed":true,"url":"#A3"},{"id":"A4","code":"A4","label":"A-4 BLS","selected":false,"defaultCollapsed":true,"url":"#A4"},{"id":"A5","code":"A5","label":"A-5 BIFO","selected":false,"defaultCollapsed":true,"url":"#A5"},{"id":"A6","code":"A6","label":"A-6 VA","selected":false,"defaultCollapsed":true,"url":"#A6"}]},{"id":"2","code":"B","selected":false,"defaultCollapsed":true,"label":"B. ECQG","items":[{"id":"B1","code":"B1","label":"B-1 VR","selected":false,"defaultCollapsed":true,"url":"#B1"}]},{"id":"3","code":"C","selected":false,"defaultCollapsed":true,"label":"C. CRR","items":[{"id":"C1","code":"C1","label":"C-1 RSR","selected":false,"defaultCollapsed":true,"url":"#C1"},{"id":"C2","code":"C2","label":"C-2 Other","selected":false,"defaultCollapsed":true,"url":"#C2"}]}]

const result = data
  .flatMap(o => o.items) // flatten items to a single array
  .find(o => o.selected === true) // find the item

console.log(result)

The lodash version that uses the same principles:

const data = [{"id":"1","code":"A","selected":true,"defaultCollapsed":false,"label":"A","items":[{"id":"A1","code":"A1","label":"A-1 PP","selected":true,"defaultCollapsed":false,"url":"#A1"},{"id":"A2","code":"A2","label":"A-2 ST","selected":false,"defaultCollapsed":true,"url":"#A2"},{"id":"A3","code":"A3","label":"A-3 SR","selected":false,"defaultCollapsed":true,"url":"#A3"},{"id":"A4","code":"A4","label":"A-4 BLS","selected":false,"defaultCollapsed":true,"url":"#A4"},{"id":"A5","code":"A5","label":"A-5 BIFO","selected":false,"defaultCollapsed":true,"url":"#A5"},{"id":"A6","code":"A6","label":"A-6 VA","selected":false,"defaultCollapsed":true,"url":"#A6"}]},{"id":"2","code":"B","selected":false,"defaultCollapsed":true,"label":"B. ECQG","items":[{"id":"B1","code":"B1","label":"B-1 VR","selected":false,"defaultCollapsed":true,"url":"#B1"}]},{"id":"3","code":"C","selected":false,"defaultCollapsed":true,"label":"C. CRR","items":[{"id":"C1","code":"C1","label":"C-1 RSR","selected":false,"defaultCollapsed":true,"url":"#C1"},{"id":"C2","code":"C2","label":"C-2 Other","selected":false,"defaultCollapsed":true,"url":"#C2"}]}]

const result = _.find(_.flatMap(data, 'items'), 'selected')

console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

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

1 Comment

Flatmap loops all the items in the data array, which may not be necessary. This is an elegant solution, not the most efficient one.
1

You can achieve this with two nested reduce() functions:

const data = [{"id":"1","code":"A","selected":true,"defaultCollapsed":false,"label":"A","items":[{"id":"A1","code":"A1","label":"A-1 PP","selected":true,"defaultCollapsed":false,"url":"#A1"},{"id":"A2","code":"A2","label":"A-2 ST","selected":false,"defaultCollapsed":true,"url":"#A2"},{"id":"A3","code":"A3","label":"A-3 SR","selected":false,"defaultCollapsed":true,"url":"#A3"},{"id":"A4","code":"A4","label":"A-4 BLS","selected":false,"defaultCollapsed":true,"url":"#A4"},{"id":"A5","code":"A5","label":"A-5 BIFO","selected":false,"defaultCollapsed":true,"url":"#A5"},{"id":"A6","code":"A6","label":"A-6 VA","selected":false,"defaultCollapsed":true,"url":"#A6"}]},{"id":"2","code":"B","selected":false,"defaultCollapsed":true,"label":"B. ECQG","items":[{"id":"B1","code":"B1","label":"B-1 VR","selected":false,"defaultCollapsed":true,"url":"#B1"}]},{"id":"3","code":"C","selected":false,"defaultCollapsed":true,"label":"C. CRR","items":[{"id":"C1","code":"C1","label":"C-1 RSR","selected":false,"defaultCollapsed":true,"url":"#C1"},{"id":"C2","code":"C2","label":"C-2 Other","selected":false,"defaultCollapsed":true,"url":"#C2"}]}]


const res = data.reduce((acc1,cur1) => {
    if(cur1.selected){
        return cur1.items.reduce((acc2,cur2) => {
            return cur2.selected ? acc2.concat(cur2.id) : acc2
        },[])
    }else{
        return acc1
    }
},[])

console.log(res)

1 Comment

Loop all the items regardless of whether something is found.
1

There is not much to improve upon. Find will stop at the first value being true.

The only room for improvement might be to make it more readable and only use native JavaScript.

const data = [
  {
    "id": "1",
    "code": "A",
    "selected": true,
    "defaultCollapsed": false,
    "label": "A",
    "items": [
      {
        "id": "A1",
        "code": "A1",
        "label": "A-1 PP",
        "selected": true,
        "defaultCollapsed": false,
        "url": "#A1"
      },
      {
        "id": "A2",
        "code": "A2",
        "label": "A-2 ST",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#A2"
      },
      {
        "id": "A3",
        "code": "A3",
        "label": "A-3 SR",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#A3"
      },
      {
        "id": "A4",
        "code": "A4",
        "label": "A-4 BLS",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#A4"
      },
      {
        "id": "A5",
        "code": "A5",
        "label": "A-5 BIFO",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#A5"
      },
      {
        "id": "A6",
        "code": "A6",
        "label": "A-6 VA",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#A6"
      }
    ]
  },
  {
    "id": "2",
    "code": "B",
    "selected": false,
    "defaultCollapsed": true,
    "label": "B. ECQG",
    "items": [
      {
        "id": "B1",
        "code": "B1",
        "label": "B-1 VR",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#B1"
      }
    ]
  },
  {
    "id": "3",
    "code": "C",
    "selected": false,
    "defaultCollapsed": true,
    "label": "C. CRR",
    "items": [
      {
        "id": "C1",
        "code": "C1",
        "label": "C-1 RSR",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#C1"
      },
      {
        "id": "C2",
        "code": "C2",
        "label": "C-2 Other",
        "selected": false,
        "defaultCollapsed": true,
        "url": "#C2"
      }
    ]
  }
]

console.log(_.find(_.find(data, item => _.find(item.items, item1 => item1.selected)).items, item3 => item3.selected).code)

// More readable than above
// Find is the best option as it stops once the condition is true
function findSelected(dataToSearch) {
   let childIndex = -1;
   const parentIndex = dataToSearch.findIndex((codedData) => {
       return childIndex = codedData.items.findIndex(anotherCodedData => anotherCodedData.selected)
   });
   if (parentIndex != -1 && childIndex =! -1) { return dataToSearch[parentIndex].items[childIndex] }
   return null
}
const foundData = findSelected(data);
if (foundData) { console.log(foundData.code) }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

2 Comments

Array.findIndex() returns -1 when no item is found. In addition 0 is a legitimate index. This means that parentIndex && childIndex will give a false positive when the item wasn't found, which will cause the return statement to error. On the other hand, if the parent or the child has an index of 0, the return value would be null. If you want to manually iterate the items and return the item when it's found use two for...of loops.
You are correct! -1 is even true-ish in JavaScript. Correcting my mistake

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.