Chris
Chris

Reputation: 815

Scala - Creating a new list by combining the contents of one list and the hierarchy of another

I am trying to create a new list that combines the contents of one single-level list with the hierarchy of another nested list and link them by the IDs of objects in both lists. Note: the ID for the BChain object is stored within the ItemObj inside it.

I have three objects:

ItemObj(id: String, stored: DateTime)
BChain(item: ItemObj, List[BChain])
AChain(aid: String, List[AChain])

I have two lists:

val nestedList: List[AChain]
val singleLevelList: List[BChain]

I want the second list to have the hierarchy of the first, but still contain it's own elements. Therefore, BChain should include the original ItemObj and List[BChain] properties (including all of ItemObj's original property data - ID and DateTime) when it is put into the newly desired list output.

So Instead of (input):

 val nestedList = List(
 AChain("123", List(AChain("456", [])),
 AChain("789", [])
)

val singleLevelList = List(
 BChain(ItemObj("123", DateTime), []),
 BChain(ItemObj("456", DateTime), []),
 BChain(ItemObj("789", DateTime), []))
)

I would like the following output:

val combinedLists = List(
     BChain(ItemObj("123", DateTime), List(BChain(ItemObj("456", DateTime), [])),
     BChain(ItemObj("789", DateTime), [])
)

as the final list.

Note: There may be more items in the nested list than the single-level list, and if this is the case then the extra items should be ignored. Each of the items in the single-level list should correspond to one of the items in the nested list.

How can I accomplish this?

Any help would be appreciated.

Upvotes: 0

Views: 408

Answers (3)

jwvh
jwvh

Reputation: 51271

First I had to dummy up a DateTime type and value so that the following would compile.

case class ItemObj(id: String, stored: DateTime)
case class BChain(item: ItemObj, bcl :List[BChain])
case class AChain(aid: String, acl :List[AChain])

Then I changed your singleLevelList into real Scala code and created a Map for fast lookup.

val singleLevelList = List(
  BChain(ItemObj("123", DateTime), Nil),
  BChain(ItemObj("456", DateTime), Nil),
  BChain(ItemObj("789", DateTime), Nil)
)

val sLLMap = singleLevelList.groupBy(_.item.id)

Next a recursive method to change all AChains to BChains.

def a2b(aLst :List[AChain]) :List[BChain] =
  aLst.map(a => BChain(sLLMap(a.aid).head.item, a2b(a.acl)))

Now to test it.

val nestedList = List(
  AChain("123", List(AChain("456", Nil))),
  AChain("789", Nil)
)

a2b(nestedList)  //appears to work

Of course this will throw if nestedList has an A without a corresponding B in the singleLevelList.

Upvotes: 1

Dmitry Reutov
Dmitry Reutov

Reputation: 3032

This code will work even if root element of nested list is absent in single level list

final case class ItemObj(id: String, stored: DateTime = null)
final case class AChain(aid: String, children: List[AChain] = List())
final case class BChain(bid: ItemObj, children: List[BChain] = List())

val nestedList = List(
  AChain("123", List(AChain("456"))),
  AChain("789")
)

def getCombineList(nestedList: List[AChain], singleList: List[BChain]): List[BChain] = {
  val singleListMap = singleList.groupBy(_.bid.id)

  def combine(items: List[AChain]): List[BChain] = {
    items flatMap {item => 
      val children = combine(item.children)
      val parent = singleListMap.get(item.aid).map(v =>
        List(v.head.copy(children = children))
      ).getOrElse(
        children
      )
      parent
    }
  }

  combine(nestedList)
}

val s = getCombineList(nestedList, List(BChain(ItemObj("456")), BChain(ItemObj("789"))))

Upvotes: 1

Maybe something like this is what you want?

final case class ItemObj(id: String, stored: DateTime)
final case class BChain(item: ItemObj, list: List[BChain])
final case class AChain(aid: String, list: List[AChain])

def combine(as: List[AChain], bs: List[BChain]): List[BChain] = {
  val asMap = as.iterator.map(a => a.aid -> a.list).toMap

  def toBChain(a: AChain): BChain =
    BChain(
      item = ItemObj(id = a.aid, stored = ???),
      list = a.list.map(toBChain)
    )

  bs.map {
    case BChain(item, list) =>
      val newElements =
        asMap
          .getOrElse(key = item.id, default = Nil)
          .map(toBChain)

      BChain(
        item,
        list ::: newElements
      )
  }
}

Upvotes: 1

Related Questions