arunmmanoharan
arunmmanoharan

Reputation: 2675

Find property key name from nested array of objects

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.

Upvotes: 0

Views: 111

Answers (3)

Jeffrey Devloo
Jeffrey Devloo

Reputation: 1426

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>

Upvotes: 1

symlink
symlink

Reputation: 12209

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)

Upvotes: 1

Ori Drori
Ori Drori

Reputation: 191976

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>

Upvotes: 3

Related Questions